PPIx-Regexp-0.082000755000765000024 014151156043 12656 5ustar00tomstaff000000000000PPIx-Regexp-0.082/Build.PL000444000765000024 214514151156043 14311 0ustar00tomstaff000000000000use 5.006; use strict; use warnings; use lib qw{ inc }; use Module::Build; use My::Module::Build; use My::Module::Meta; ( my $mbv = Module::Build->VERSION() ) =~ s/_//g; my $meta = My::Module::Meta->new(); my %args = ( add_to_cleanup => $meta->add_to_cleanup(), build_requires => $meta->build_requires(), configure_requires => $meta->configure_requires(), dist_abstract => $meta->abstract(), dist_author => $meta->author(), dist_name => $meta->dist_name(), license => $meta->license(), module_name => $meta->module_name(), requires => $meta->requires( perl => $meta->requires_perl(), ), script_files => $meta->script_files(), ); if ( $mbv >= 0.28 ) { $args{meta_merge} = $meta->meta_merge(); $args{no_index} = $meta->no_index(); $args{meta_add} = { $meta->provides(), }, } # Don't require Module::Build when building distribution, because user # can use ExtUtils::MakeMaker. $mbv >= 0.34 and $args{auto_configure_requires} = !$meta->distribution(); my $bldr = My::Module::Build->new( %args ); $bldr->create_build_script(); # ex: set textwidth=72 : PPIx-Regexp-0.082/CONTRIBUTING000444000765000024 271014151156043 14645 0ustar00tomstaff000000000000I welcome bug reports, patches, and suggestions. My preferred way to recieve these is via the RT system at https://rt.cpan.org/Public/Dist/Display.html?Name=PPIx-Regexp, but I happily accept them either through GitHub at https://github.com/trwyant/perl-PPIx-Regexp, by electronic mail to WYANT AT cpan DOT org, or any other way that works for you, though I can not accept owls because I have no facilities to house them. Non-RT requests may be turned into RT tickets by me unless you specifically request otherwise. The bug report, patch, or suggestion (if acted on) will also be acknowledged in the Changes file unless you specifically request othewise. Requests for information probably do not need to be tickets in RT or GitHub, and my preferred route for these is by electronic mail, but again anything that works for you is probably fine with me. I try hard never to reject a bug report outright, though I may edit patches, believe that a different fix is more in line with my vision for the code, or even that the report is due to a misunderstanding and address it with a documentation change. Whatever I decide I will give you time to respond (typically a week or so), and whatever I actually do I will give you time to see if it meets your needs before I do a production release. GitHub pull requests should be made on a topic branch rather than the master branch. If you have something big in mind I would appreciate a heads-up in some form prior to the pull request. PPIx-Regexp-0.082/Changes000444000765000024 10645614151156043 14362 0ustar00tomstaff0000000000000.082 2021-11-29 T. R. Wyant Add --version to eg/predump, and document all options with double dashes. Silence 'uninitialized' warning generated by /(?<=.{35})/. Thanks to Brian Fraser for reporting this. Try to quell weird Win32 test failures which seem to occur only in tests where I am using 'use open' to put the standard handles into UTF-8 mode. The fix (I hope) is to do this to the Test::Harness handles at run time instead of to the standard handles at compile time. Add file CONTRIBUTING. 0.081 2021-10-22 T. R. Wyant Any use of the postderef argument is now fatal. Correct generation of 'provides' metadata. Thanks to Favio Poletti for blogging https://github.polettix.it/ETOOBUSY/2021/06/15/the-real-pause-workaround/, and ultimately to Joel Berger for the pointer to https://metacpan.org/pod/CPAN::Meta::Spec#no_index Add YAPE::Regex to SEE ALSO 0.080 2021-04-16 T. R. Wyant All uses of the postderef argument to new() now warn. 0.079 2021-03-26 T. R. Wyant Get prerequisites up to snuff, and add xt/author/prereq.t to ensure they stay that way. Add rt.cpan.org back to bug reporting methods. Long live RT! 0.078 2021-01-28 T. R. Wyant Allow CPAN to index Script_Run, Atomic_Script_Run, since they made it into a production release. Allow {,3} and { 0 , 3 } as quantifiers, requiring at least Perl 5.33.6. Previously these parsed as literals. This parse will be retracted if it does not make it into 5.34.0. 0.077 2021-01-14 T. R. Wyant Add Travis CI testing. Use GitHub as bug tracker. R.I.P. rt.cpan.org. 0.076 2020-11-28 T. R. Wyant Correct (I hope) detection of \K in nested assertions. Variable-length look-behind is version 5.029009. Look-behinds quantified longer than 255 characters are an error, and are made into unknown tokens or structures. I ended up refactoring the PPIx::Regexp::Token::GroupType class initialization for the latter two changes. 0.075 2020-10-08 T. R. Wyant Warn on first use of attribute 'postderef'. 0.074 2020-09-08 T. R. Wyant Remove PPIx::Regexp::StringTokenizer itself and all documentation referring to it or the 'parse' argument to PPIx::Regexp->new(). 0.073 2020-07-28 T. R. Wyant Remove prototypes from testing subroutines defined in t/*.t. 0.072 2020-05-20 T. R. Wyant Drop dependency on List::MoreUtils. Thanks to Graham Ollis (plicease) for bringing the issues with this module to my attention. 0.071 2020-03-28 T. R. Wyant Recognize wildcard Unicode names (Perl 5.31.10). Try to get correct line number in derived PPI. This is done by injecting "\n" as needed. The initial #line directive becomes "#line 2", but is suppressed if I need to generate line 1. Improve normalization of content for ppi(). This involves the un-bracketing of things like ${foo}. Deprecate new() argument postderef. At this stage it is only documented as deprecated. In the first release after October 1 2020 it will warn on the first use. Eventually it will be retracted, and postfix dereferences will always be recognized. This is the default behavior now. Add dump argument/option 'short' which, if true, causes leading 'PPIx::Regexp::' to be removed from class names. 0.070 2020-02-27 T. R. Wyant Add index_locations option to PPIx::Regexp->new(). This defaults to true if the regexp is specified as a PPI::Element object. The locations are consistent with the containing PPI::Document. Add methods location(), column_number(), line_number(), logical_filename(), logical_line_number(), and visual_column_number() to PPIx::Regexp::Element. All return undef if the locations could not be determined. Add method statement() to PPIx::Regexp::Element. This returns the PPI statement containing the regexp element, or nothing if none. Add method is_matcher() to PPIx::Regexp::Element. This classifies objects as to whether they actually match something in the target string. Possible returns are true (they do), false but defined (they do not) and undef (no clue). Add methods first_token() and last_token() to PPIx::Regexp::Node. Add methods next_token() and previous_token() to PPIx::Regexp::Element. 0.069 2020-02-07 T. R. Wyant The PPIx::Regexp->new() 'parse' option is now fatal. This selected either string or regex parse. I consider the string parse a failed experiment. This is the latest step in removing it in favor of the PPIx::QuoteLike package. 0.068 2020-01-21 T. R. Wyant Expose PPIx::Regexp::Util::is_ppi_regexp_element() explain() on [[=x=]] now calls it a Character Equivalence. It's still a PPIx::Regexp::Token::CharClass::POSIX::Unknown (and therefore an error), though. 0.067 2019-08-30 T. R. Wyant \K was retracted in Perl 5.31.3, but only inside look-around assertions. 0.066 2019-08-16 T. R. Wyant Fix broken POD, and add tests to ensure it remains fixed. 0.065 2019-05-25 T. R. Wyant Quash undef error in __is_ppi_regexp_element() when passed a PPI::Token::Regexp::Transliterate Support proper version for qr'\N{name}'. Until 5.29.10 this construction failed to parse because it did not interpolate. But PPIx::Regexp blithely ignored this detail. As of 5.29.10, something like m'\N{LATIN CAPITAL LETTER L}' matches identically to m'L'. So I implemented introduction as of that version. Have explain() recognize Unicode property wildcards. 0.064 2019-04-01 T. R. Wyant Empty \p{} should be an error. \x{} and \x{ non-hex } should be errors under "use re 'strict'" \o{} should be an error \o{ non-octal } should be an error under "use re 'strict'" Support wildcard Unicode property values. These were added in 5.29.9. Add eg/find-variable-length-lookarounds Add convenience method extract_regexps(). This is a static method on PPIx::Regexp that takes as its argument a PPI::Document and manufactures PPIx::Regexp objects out of anything that parses to a regexp of some sort. Don't run illegal character tests before Perl 5.18 unless we're author testing, because they are noisy. I think the issue is not the Perl version per se, but the version of Unicode; Perl5180delta says it shipped with Unicode 6.2. 0.063 2018-11-08 T. R. Wyant Silence weird-character parse tests and make them no longer author-only. Further deprecate 'parse' argument to new(). You now get a warning on each use. 0.062 2018-08-12 T. R. Wyant Remove tokenizer method prior(). This is the last step in its deprecation. 0.061 2018-07-09 T. R. Wyant Only standalone graphemes and non-characters allowed as delimiters starting with Perl 5.29.0. Non-ASCII delimiters started working in 5.8.3, so that is what perl_version_introduced() returns for them. Collateral with all this, accept word characters as delimiters, but only with at least one space between the operator and the expression -- that is, 'qr xyx' is OK, but 'qrxyx' is not. 0.060 2018-06-16 T. R. Wyant \N{} now parses as the unknown token, not NoOp, regardless of the setting of 'use re qw< strict >;'. \N{} became unconditionally fatal in 5.28.0 (5.27.1, actually). The policy when the parse changes is to use the most-modern parse. Hence this change. As a side effect of this, the unknown token's explain() method now returns something -- normally the associated error. Add method remove_insignificant(). If the invocant isa Node, this returns a clone of the invocant with non-significant elements removed. Otherwise it returns either the invocant or nothing. 0.059 2018-05-08 T. R. Wyant Install @CARP_NOT everywhere so that warnings and exceptions generated in the bowels of the system appear to come from the point where the system is entered. Further deprecate string (versus regexp) parsing. The first use of the 'parse' argument to new() will result in a warning. If the value of the argument is 'guess' or 'string', the warning refers to PPIx::QuoteLike. 0.058 2018-04-26 T. R. Wyant Prefer /[0-9]/ over /\d/ for numeric checks. The latter can match non-ASCII digits. Explain the negated POSIX character classes. Also tweak some of the asserted explanations -- mostly for readability and parallel construction with the negated explanations, but it turns out [[:digit:]] is NOT equivalent to [0-9]. 0.057 2018-04-17 T. R. Wyant Allow ->asserts( 'a*' ). This modification actually allows wild cards in asserts() on all match semantic modifiers, but it is probably only useful in the case of 'a*', because that is the only one that can be doubled. Explain grouping structure as 'Grouping', not 'Capture or grouping'. Caret modifier was not turning off /n. This was complicated by the fact that (?^) was introduced in 5.13.6, but (?n) was not introduced until 5.21.8. The solution was to include -n in the expansion of the caret if and only if /n had been seen in the scope of the caret. Recognize caret in /(?^)x/. Acknowledge Regexp::Parsertron in SEE ALSO 0.056 2018-03-07 T. R. Wyant Support removal of unescaped literal left curlys after left parens, which was deprecated in 5.27.8. No actual change in output yet, since deprecation is not tracked, but the perl_version_removed() logic is there. Add next_element() and kin. These are analogous to next_sibling() and kin, but will cross over from content proper into structure (beginning and end delimiters, etc) and vice versa. Correct requirements_for_perl() for impossible regular expression. It now returns '! $]' when the components of the regexp are valid, but none are valid under any specific version of Perl. It used to think all Perls were OK when this happened. Add the alpha_assertions introduced in 5.27.9. Handle 5.27.9's change from +script_run to *script_run, and support *sr as a synonym. 0.055 2018-02-08 T. R. Wyant Tokenizer method prior() is now fatal. This was documented as package-private, but as it WAS documented, I am putting it through a deprecation cycle anyway. Six months from now it will be removed. Add Script_Run classes as subclasses to their superclass docs. This was missed in the last update. 0.054 2018-01-29 T. R. Wyant Add support for (+script_run:...). This is an experimental feature added in Perl 5.27.8. It imposes on any matches it contains the additional restriction that everything matched has to belong to the same Unicode script. This support will be retracted if the functionality does not make it into Perl 5.28. Add method scontent(). This returns significant content only. That is, if called on the parse of '/ f u b a r /x', it returns '/fubar/x'. 0.053 2017-10-30 T. R. Wyant Recognize \px as Unicode char class. At least, when the x is C, L, M, N, P, S or Z. The 'parse' argument to new() is now deprecated. 0.052 2017-09-07 T. R. Wyant RT 122715: Clarify Node->find_parents() documentation. Thanks to Salvatore Bonaccorso for letting me know about this problem. Further deprecate tokenizer method prior() in favor of prior_significant_token(). Add requirements_for_perl(). This is analogous to the CPAN::Meta::Requirements method requirements_for_module(), though the output is formatted differently. Also put in the actual requirements for an un-escaped literal left curly after a constant, which was removed in 5.25.1 and reinstated in 5.27.1. Add accepts_perl(). This is analogous to CPAN::Meta::Requirements->accepts_module(). I decided that CPAN::Meta;:Requirements was overkill, but this may turn out to be the wrong decision, so I will be careful what I expose. Document behavior of perl_version_introduced() and perl_version_removed() when a feature is re-introduced after removal, or re-removed after re-introduction. \N{} (empty curlys) removed in 5.27.1. 0.051 2017-01-29 T. R. Wyant Support whitespace inside [] if /xx in effect. Starting with Perl 5.25.9, a space or tab appearing inside a bracketed character class is not significant if /xx is asserted. Further deprecate tokenizer method prior() Add 'provides' data to ExtUtils::MakeMaker output SOME unescaped litaral '{' removed in 5.025001. After '.', Unicode classes, and bracketed classes (including extended) they are still legal. Make /\b{/ an error Perl fails to parse the above, because once it sees the '\b{' it wants to find one of the extended boundary assertions (like \b{wb}), and declares an error when it does not. So we check for this and rebless the curly into an unknown token, not a literal. 0.050 2016-05-06 T. R. Wyant Parse bracketed substitution with embedded comment. This is something like s{foo}\n#{bar}\n{baz} which is equivalent to s/foo/baz/. PPI gets this wrong, and we're not smart enough to fix up the PPI parse, but if given this as text, we now parse it correctly. We now recognize postfix dereferences by default, since Perl does beginning with 5.24. In other words, default new() argument 'postderef' to true. Unterminated substitutions (i.e. 's//') should no longer cause an exception. Instead they parse as an unknown token. 0.049 2016-04-19 T. R. Wyant Robustify PPIx::Regexp->perl_version_removed() The problem here was that if the expression being parsed was sufficiently badly-formed, $self->delimiters() would be undef, throwing a warning. Correct dump of embedded modifiers (eg: (?i:...)) 0.048 2016-02-29 T. R. Wyant Add option 'strict', like 'use re "strict"' In the presence of strict(), I opted to set perl_version_introduced to the version of Perl where the construct became an error. Parse '\N{}' as no-op. The previous parse was a character class ('\N') followed by two literals ('{' and '}'). But perl5238delta said that it had been ignored up to that time. Starting with 5.23.8 it is an error if 'use re strict' is in effect. Quash 'NOT a POSIX class ...' warning under 5.23.8 Add Makefile targets authortest and testcover. 0.047 2016-01-29 T. R. Wyant Recognize \b{lb}, introduced in 5.23.7. If this is retracted before 5.24, it will be removed outright. 0.046 2016-01-08 T. R. Wyant Add GitHub repository to mmetadata. 0.045 2015-12-31 T. R. Wyant Deprecate tokenizer method prior() in favor of prior_significant_token(). This is not part of the public interface, so I suppose I could have just slam-dunked it, but ... Add ability to parse strings as well as regexes The new functionality is controlled by the new new() argument 'parse', whose permitted values are 'regex' (the default), 'string', or 'guess'. String parsing, and the 'string' and 'guess' values of 'parse', are experimental. 0.044 2015-12-08 T. R. Wyant Allow nesting of \Q with \U, \L, and \F The perlop docs say these nest with each other. Playing with Perl suggests that \U, \L and \F supersede each other, but thet they as a group nest with \Q in either order, so that if you specify \Q and one of the \U, \L, \F group you need two \Es to turn them all back off. Restrict recognition of back references in replacement strings to \number form, since Perl itself does not recognize \g{...} or \k{...} there. Recognize postfix dereference if desired. This is controlled by the Boolean argument 'postderef' passed to PPIx::Regexp->new(). The default is false, but will become true if postfix dereference becomes mainstream Perl 5. Add explain() and supporting methods main_structure() and in_regex_set(). The explain() method returns a brief explanation of what the element does. 0.043 2015-11-18 T. R. Wyant Do not end regex set prematurely on finding '])' The problem is that '])' can occur within an extended bracketed character class if it contains grouping parentheses and the last item in a group is a regular bracketed character class and there is no white space between the end of the character class and the end of the group. Record parse failure if switch condition is unknown The structure was being reblessed to PPIx::Regexp::Structure::Unknown, but the number of parse failures was not being incremented. Parse \U and friends as meta-characters inside \Q...\E This turns out to be what Perl itself does, as shown by $ perl -E 'say qr{\Q\Ufoo}' Clear error when lexer identifies unknown token. Those who peruse the changes in this release will see that a bunch of refactoring was done as part of this. Parse white space inside bracketed character classes inside extended bracketed character classes (whew!) as literals, except for the space character itself and the horizontal tab. This tracks the corresponding change in Perl 5.23.4. This will be reverted if the corresponding Perl change does not make it into 5.24.0. Beginning with version 0.035, PPIx::Regexp was incorrectly reporting the sense of modifiers when the same token both asserted and negated modifiers (e.g. '(?x-i:...)'). This release should correct the problem. Document policy when Perl changes in such a way that the proper parse for a regular expression changes. In this case the more modern parse is preferred. 0.042 2015-10-09 T. R. Wyant Report error rather than failing when parsing a string consisting wholly of white space. Group types were not being recognized if they contained the delimiter character for the regexp (e.g. in qr<(?\ the look-behind assertion was not recognized as such). Correct mis-parse of ' s///'. Leading white space is supposed to be acceptable, but the leading whitespace token caused PPIx::Regexp::Lexer not to recognize the substitution as such. Tokenizer was failing when the string to be parsed was so bad it was trying to return the whole thing as a single PPIx::Regexp::Token::Unknown. PPIx::Regexp::Dumper now displays a message if a structure is missing its end delimiter. RT 107331 Produce parse error in the presence of trailing cruft. Thanks to Klaus Rindfrey for catching this. The tokenizer now does a preliminary scan for delimiting brackets and modifiers. Anything after the modifiers except for white space is now made into a PPIx::Regexp::Token::Unknown, resulting in a parse failure being reported. The previous implementation simply assumed a valid expression, and in the case of the expression in the ticket blithely mismatched the delimiters and returned a parse without failures, but which was manifestly bogus. Tweak documentation in PPIx::Regexp. 0.041 2015-07-02 T. R. Wyant Report \C (match octet) as removed in 5.23.0. Accept non-ASCII whitespace under /x. The Whitespace object can be multiple characters; the perl_version_introduced() becomes '5.021001' if any of them is a code point above 127. The perl_version_removed() method now returns '5.021001' when called on a PPIx::Regexp object produced by parsing '?foo?' (match once without explicit 'm'). The object produced by parsing 'm?foo?' still returns the minimum Perl version. 0.040 2015-05-31 T. R. Wyant Do not parse unadorned parentheses as capture groups when /n is in effect. Instead, they are parsed as PPIx::Regexp::Structure. Named captures appear to be unaffected by /n. Made a verbose dump a little more so. Specifically, dump max_capture_group where relevant, and display dumped values a bit more informatively. Report /n (no captures) as having been added in 5.21.8. 0.039 2015-04-02 T. R. Wyant Recognize nested subscripts in interpolation. Thanks to Andy Lester for finding this, which actually manifested in Perl-Critic-Policy-Variables-ProhibitUnusedVarsStricter. The problem is that the actual heuristics for finding the end of an interpolation are undocumented, and I missed this rather-obvious case. Add \b{g} (= \b{gcb}) 0.038 2015-03-09 T. R. Wyant Make \b{foo} into an unknown token (and therefore an error. This applies to \b{anything}, where 'anything' is anything bur 'gcb', 'wb', or 'sb'. Handle the boundary assertions introduced in Perl 5.21.9: '\b{gcb}' (grapheme cluster boundary), '\b{wb}' (word boundary), '\b{sb}' (sentence boundary), and the corresponding '\B{...}' constructions. Similar-looking things like '\b{foo}' are not recognized as assertions, and end up being literals. This is less general than I usually make things, but was done against the possibility that (e.g.) '\b{foo}' might be introduced later, requiring perl_version_released() to return a different number. Any of these retracted prior to Perl 5.22.0 will simply be removed from PPIx::Regexp. 0.037 2014-11-12 T. R. Wyant Have PPIx::Regexp::Structure::RegexSet POD recognize that the Perl docs (specifically perlrecharclass) now call this construction Extended Bracketed Character Classes, not sets. Correctly mark the replacement portion of s///ee as code. Prior to this release it was parsed as though no /e were present. Make available the number of times a given modifier is asserted (except for the match semantics modifiers which get handled differently). See PPIx::Regexp::Token::Modifier->asserted() and PPIx::Regexp::Tokenizer->modifier() for details. 0.036 2014-01-04 T. R. Wyant Retract the "Allow non-ASCII white space under /x" change introduced in version 0.033. I misread perl5170delta, and implemented early. Change to explicit character class to recognize white space under /x. I was previously using \s, which matched too much. Thanks to Nobuo Kumagai for finding and reporting this. 0.035 2013-11-15 T. R. Wyant Properly handle multi-character modifiers like /ee. We now handle /eie as being the same as /eei. Thanks to Anonymous Monk for finding this. Properly handle \g and \k back references that do not correspond to an actual capture group. They are now reblessed into the unknown token, and counted as errors. Thanks to Anonymous Monk for finding this. Add method error() to PPIx::Regexp::Element. This should return an error message when the element is in error -- normally when it has been blessed into the unknown token or structure. Add method modifier_asserted() to PPIx::Regexp::Element. This walks the parse tree backward to determine if the given modifier is in effect for the element. 0.034 2013-05-11 T. R. Wyant Correct spelling and grammar errors in POD and comments. RT #85050. Thanks David Steinbrunner for catching these. Allow interpolation in regex sets. It implies Perl 5.17.9 or higher. Allow non-ASCII white space under /x. It implies Perl 5.17.9 or higher. Fix problems with Regex Set functionality under Perl 5.6.2. CPAN testers RULE! 0.031 2013-01-31 T. R. Wyant Have PPIx::Regexp::Token::Code (and offspring) become PPIx::Regexp::Token::Unknown inside a regex set. 0.030 2013-01-22 T. R. Wyant Add Regex Sets, which were added to Perl as an experimental feature in 5.17.8. This is experimental in Perl, therefore the parse may change. Ditch PPIx::Regexp::Token::GroupType method __expect_after_match() in favor of the more general __match_setup(). This is done without deprecation because __expect_after_match() was documeted as package-private, but noted in the change log because it _was_ documented. 0.029 2013-01-14 T. R. Wyant Add method unescaped_content() to PPIx::Regexp::Element(). Rewrite the tokenizing code in PPIx::Regexp::Token::GroupType and offspring to use regular expressions specific to the regexp delimiter, and escaping only that delimiter. Thanks again to Alexandr Ciornii for finding more of these. Fix mis-parse of /(\?|I)/ as a branch reset (it's really an alternation). There may be more of these lurking. Thanks to Alexandr Ciornii for finding this one. Add options -files and -objectify to eg/predump. 0.028 2012-06-06 T. R. Wyant Replace all uses of YAML::Any with YAML, since they come in the same distro, and YAML does not suffer from deprecation warnings. 0.027 2012-05-28 T. R. Wyant Eliminate unescaped literal "{" characters in regexps in PPIx::Regexp::Token::Backreference and PPIx::Regexp::Token::CharClass::Simple. These are deprecated in 5.17.0. 0.026 2012-02-24 T. R. Wyant Add support for \F (fold case), added in 5.15.8. 0.025 2012-01-04 T. R. Wyant Tolerate leading and trailing white space around the regular expression. These are still round-trip safe, since the white space is tokenized. Make Changes file conform to CPAN::Changes, and add xt/author/changes.t to ensure continued compliance. 0.024 2011-12-17 T. R. Wyant Reinstate author test xt/author/manifest.t, which was clobbered shortly before the release of 0.021_10. 0.023 2011-12-08 T. R. Wyant Correct address of FSF in the version of the GPL distributed in LICENSES/Copying. Thanks to Petr Pisar for picking this up. 0.022 2011-11-24 T. R. Wyant Correct various documentation errors. Don't initialize effective modifiers with '^', since that wrongly asserts that /d has been seen somewhere along the line. Implement negation of match-semantic modifiers (e.g. 'no re /u;') by setting the relevant datum to undef. Support for default modifiers. This includes: * default_modifiers argument to new() in PPIx::Regexp, PPIx::Regexp::Tokenizer, and PPIx::Regexp::Dumper * Public method modifier_asserted() on PPIx::Regexp, to return whether a given modifier is actually in effect. The results of the modifier() method are unchanged. Require Test::More 0.88 for installation. Eliminate all the 'eval { require ... }' logic in favor of 'use Test::More 0.88'. Have Makefile.PL make use of {BUILD_REQUIRES} if it is available. Fix PPIx::Regexp::Token::Whitespace->can_be_quantified() to return false. 0.021 2011-07-22 T. R. Wyant Modified tokenizer to correctly handle a back slash used as a delimiter. I believe. PPIx::Regexp::Dumper now dumps the results of ppi() if that method is present and -verbose is asserted. 0.020 2011-04-02 T. R. Wyant Corrected perl_version_introduced(): * \R is now 5.009005 (was 5.000). 0.019 2011-03-01 T. R. Wyant Various corrections to perl_version_introduced(): * \X is now 5.006 (was 5.000); * \N{name} is now 5.006001 (was 5.006); * \N{U+xxxx} is now 5.008 (was 5.006). The \C is now parsed as a PPIx::Regexp::Token::CharClass::Simple. It was previously considered a PPIx::Regexp::Token::Literal. Ensure that \N{$foo} parses as a Unicode literal, not a quantified \N. The ordinal() method returns undef for this. Understand the /aa modifier, introduced with 5.13.10. Report perl_version_introduced() of 5.013010 for the new semantic modifiers when modifying the entire expression. Correct handling of interpolations like ${^foo} and $#{foo}. 0.018 2011-02-16 T. R. Wyant Override ppi() in PPIx::Regexp::Token::Interpolation to provide the proper PPI when variable names are bracketed. Properly parse bracketed variable names (I hope!), which may not be subscripted. Take account of possible '$' or '@' casts before a symbol in an interpolation (e.g. $$foo{bar}, which is equivalent to $foo->{bar}). Add the /a modifier to PPI::Regexp::Token::Modifiers, legal only in the (?:...) construction. This was introduced in Perl 5.13.9. When parsing an interpolation from a replacement string (rather than a regular expression), take subscripts at face value rather than trying to disambiguate them from quantifiers and character classes, which they can't be in this context. 0.016 2011-01-05 T. R. Wyant The PPIx::Regexp::Token::Code perl_version_introduced() method now returns the minimum Perl version (currently set to 5.000) if it is used to represent the substitution portion of s///e. 0.015 2010-10-25 T. R. Wyant Documented intent to revoke support for features introduced in a development Perl which do not make it to a production release. This is necessary because in this case the syntax could be reused with different semantics. Added support for Perl 5.13.6 (?^...) construction. Added support for Perl 5.13.6 d, l, and u modifiers. Fixed inconsistency in perl_version_introduced() results between PPIx::Regexp::Token::Modifier and PPIx::Regexp::Token::GroupType::Modifier. Corrected PPIx::Regexp::Constant RE_CAPTURE_NAME docs, somehow missed back at 0.010_01. 0.014 2010-10-14 T. R. Wyant Recognize \o{...} as a PPIx::Regexp::Token::Literal, with perl_version_introduced() of 5.0013003. Terminate \0.. through \7.. after three characters, as Perl does. These two were brought to my attention by Brian D. Foy's "The Effective Perler" for October 11 2010, http://www.effectiveperlprogramming.com/blog/697 Correct the PPIx::Regexp::Token::Literal ordinal() method for '\b'. As a literal, this is a back space. 0.013 2010-10-10 T. R. Wyant Declare a parse failure if characters are found between the '}' and the ')' of (?{...}) and (??{...}), and rebless the tokens to ::Unknown. Perl does not accept anything here, so I think I should not either. Whitespace tweak in the PPIx::Regexp::Dumper test output for the failures test. Replace the PPI logic in PPIx::Regexp::Token::Code with a call to $tokenizer->find_matching_delimiter(). This is actually the way Perl works, as a look at toke.c and regcomp.c makes clear. Push the perl_version_introduced() back to 5.0 at the request of Alexandr Ciornii, for the potential benefit of Perl::MinimumVersion. This was done mostly by reading the various perlre, perldelta, and perlop documents, so these should be taken with a HUGE grain of salt. 0.012 2010-09-26 T. R. Wyant Track all the features reported as introduced (or removed) in Perl 5.010 back to Perl 5.009005, and report them as such. Report modifier /r as having been introduced in Perl 5.013002, rather than the default of 5.006. 0.011 2010-09-16 T. R. Wyant Remove dependencies on Params::Util and Readonly. The latter was requested by ADAMK for the benefit of Padre. It involved changing the symbols exported from PPIx::Regexp::Constant, but these were documented as private, so ... Parse POSIX character classes [=a=] and [.a.] as PPIx::Regexp::Token::CharClass::POSIX::Unknown, which counts as a parse failure since these are not supported by Perl. Make the PPI::Document created by PPIx::Regexp::Token::Code->ppi() be read only. This means we need PPI 1.116. Cache the document, and ensure the cached result is returned on subsequent calls. 0.010 2010-08-06 T. R. Wyant Fix fatal error in PPIx::Regexp::Token::Code->ppi(). Move author tests from xt/ to xt/author/. 0.009 2010-08-03 T. R. Wyant Recognize s/.../.../ee as being different from s/.../.../e. In particular, the replacement portion of the former is _not_ a Perl expression: it's an interpolatble string, which later gets eval{}'ed. 0.008 2010-07-01 T. R. Wyant Promote methods can_be_quantified() and is_quantifier() from PPIx::Regexp::Token to PPIx::Regexp::Element, so all classes inherit them. They still return true and false respectively. Override can_be_quantified() to return false on PPIx::Regexp, PPIx::Regexp::Structure::Quantifier, PPIx::Regexp::Structure::Regexp, and PPIx::Regexp::Structure::Replacement. Override is_quantifier() to return true on PPIx::Regexp::Structure::Quantifier. Modify PPIx::Regexp::Dumper to be able to display can_be_quantified and is_quantifier for PPIx::Regexp::Node objects when dumping verbosely. Convert internal data to Readonly in PPIx::Regexp::Lexer, PPIx::Regexp::Token::CharClass::Simple, PPIx::Regexp::Token::Structure, and PPIx::Regexp::Tokenizer. Remove leftover boilerplate in PPIx::Regexp::Token::CharClass::Simple. Explicitly require a minimum Perl of 5.006. Centralized dependencies in inc/PPIx/Regexp/Meta.pm. Removed claim that PPIx::Regexp is alpha code. Docs still say that the interface can be changed, but now it will go through a deprecation cycle. 0.007 2010-04-28 T. R. Wyant PPIx::Regexp::Lexer no longer fails when encountering expressions like m{)}. Instead, it marks the right parenthesis as an unmatched delimiter. Fixed RT 56864 - PPIx::Regexp::Lexer fails in Perl::Critic under Perl 5.13.0. This was due to the value of a returned $+[0] getting transmogrified before the caller saw it. I never did isolate what triggered the bug. You can now get a tokenizer trace by setting environment value PPIX_REGEXP_TOKENIZER_TRACE to a non-zero numeric value. This is unsupported, though. 0.006 2010-02-26 T. R. Wyant Parse \N{...} in accordance with perl5115delta. The curlys must contain an alpha followed by alphanumerics, spaces, parens, colons, or dashes. \N{ without a matching } is a character class (if legal) followed by a literal '{'. Parse \N inside a character class as PPI::Regexp::Token::Unknown, since Perl 5.11.5 considers this a compile error. A \N{...} inside a character class is still OK. Add method match() to PPIx::Regexp::Tokenizer. This is analogous to capture(), but returns the entire matched string. 0.005 2009-12-26 T. R. Wyant Recognize \N (without curlys), back-ported from Perl 6 into 5.11. Recognize unicode characters as \N{[[:alpha:]] ... rather than \N{[\w\s:] ... This is per the 5.11 documentation, but I think Perl always worked this way. Recognize loose matching of Unicode character classes, and allow '=' in lieu of a single ':' in a Unicode character class (this from Perl 5.11.3). PPIx::Regexp::Dumper now produces the proper output when called with perl_version => 1, test => 1. Describe the typical content of the object in the documentation for PPIx::Regexp::Structure::NamedCapture and PPIx::Regexp::Token::GroupType::NamedCapture. 0.004 2009-11-09 T. R. Wyant Have PPIx::Regexp::Token::Literal correctly recognize when charnames::vianame() is unavailable, and decouple this from the handling of \N{U+hhhh}. Add dependency on Task::Weaken, since depending on Scalar::Util appears not to cut it. Correct the assignment of the license type in Makefile.PL. 0.003 2009-11-05 T. R. Wyant Have PPIx::Regexp::Token::Literal recognize \N{U+hhhh} (where hhhh represents hex digits), and provide its ordinal (hhhh). Remove recognition of \N. (. = any character), which Perl does not do. Fix $re->flush_cache() so that it actually removes $re and only $re from the cache. Add delimiters() method to PPIx::Regexp::Main and PPIx::Regexp. Support this in eg/prenav. Increase test coverage and remove dead code. Count tests in t/parse.t and t/unit.t 0.002 2009-10-28 T. R. Wyant In verbose mode, have PPIx::Regexp::Dumper dump the absolute capture number referred to by a numbered reference. Have eg/preslurp pass its -verbose option to PPIx::Regexp::Dumper Don't use Test::More::isa_ok for the t/basic.t class heritage tests, since some versions of Test::More require a reference for the first argument of isa_ok(). 0.001 2009-10-21 T. R. Wyant Initial release. # ex: set textwidth=72 autoindent : PPIx-Regexp-0.082/MANIFEST000444000765000024 603714151156043 14152 0ustar00tomstaff000000000000Build.PL Changes CONTRIBUTING eg/find-variable-length-lookarounds eg/interpolated eg/preaccepts eg/predump eg/prenav eg/preslurp eg/README inc/My/Module/Build.pm inc/My/Module/Meta.pm inc/My/Module/Mock_Tokenizer.pm inc/My/Module/Test.pm lib/PPIx/Regexp.pm lib/PPIx/Regexp/Constant.pm lib/PPIx/Regexp/Dumper.pm lib/PPIx/Regexp/Element.pm lib/PPIx/Regexp/Lexer.pm lib/PPIx/Regexp/Node.pm lib/PPIx/Regexp/Node/Range.pm lib/PPIx/Regexp/Node/Unknown.pm lib/PPIx/Regexp/Structure.pm lib/PPIx/Regexp/Structure/Assertion.pm lib/PPIx/Regexp/Structure/Atomic_Script_Run.pm lib/PPIx/Regexp/Structure/BranchReset.pm lib/PPIx/Regexp/Structure/Capture.pm lib/PPIx/Regexp/Structure/CharClass.pm lib/PPIx/Regexp/Structure/Code.pm lib/PPIx/Regexp/Structure/Main.pm lib/PPIx/Regexp/Structure/Modifier.pm lib/PPIx/Regexp/Structure/NamedCapture.pm lib/PPIx/Regexp/Structure/Quantifier.pm lib/PPIx/Regexp/Structure/Regexp.pm lib/PPIx/Regexp/Structure/RegexSet.pm lib/PPIx/Regexp/Structure/Replacement.pm lib/PPIx/Regexp/Structure/Script_Run.pm lib/PPIx/Regexp/Structure/Subexpression.pm lib/PPIx/Regexp/Structure/Switch.pm lib/PPIx/Regexp/Structure/Unknown.pm lib/PPIx/Regexp/Support.pm lib/PPIx/Regexp/Token.pm lib/PPIx/Regexp/Token/Assertion.pm lib/PPIx/Regexp/Token/Backreference.pm lib/PPIx/Regexp/Token/Backtrack.pm lib/PPIx/Regexp/Token/CharClass.pm lib/PPIx/Regexp/Token/CharClass/POSIX.pm lib/PPIx/Regexp/Token/CharClass/POSIX/Unknown.pm lib/PPIx/Regexp/Token/CharClass/Simple.pm lib/PPIx/Regexp/Token/Code.pm lib/PPIx/Regexp/Token/Comment.pm lib/PPIx/Regexp/Token/Condition.pm lib/PPIx/Regexp/Token/Control.pm lib/PPIx/Regexp/Token/Delimiter.pm lib/PPIx/Regexp/Token/Greediness.pm lib/PPIx/Regexp/Token/GroupType.pm lib/PPIx/Regexp/Token/GroupType/Assertion.pm lib/PPIx/Regexp/Token/GroupType/Atomic_Script_Run.pm lib/PPIx/Regexp/Token/GroupType/BranchReset.pm lib/PPIx/Regexp/Token/GroupType/Code.pm lib/PPIx/Regexp/Token/GroupType/Modifier.pm lib/PPIx/Regexp/Token/GroupType/NamedCapture.pm lib/PPIx/Regexp/Token/GroupType/Script_Run.pm lib/PPIx/Regexp/Token/GroupType/Subexpression.pm lib/PPIx/Regexp/Token/GroupType/Switch.pm lib/PPIx/Regexp/Token/Interpolation.pm lib/PPIx/Regexp/Token/Literal.pm lib/PPIx/Regexp/Token/Modifier.pm lib/PPIx/Regexp/Token/NoOp.pm lib/PPIx/Regexp/Token/Operator.pm lib/PPIx/Regexp/Token/Quantifier.pm lib/PPIx/Regexp/Token/Recursion.pm lib/PPIx/Regexp/Token/Reference.pm lib/PPIx/Regexp/Token/Structure.pm lib/PPIx/Regexp/Token/Unknown.pm lib/PPIx/Regexp/Token/Unmatched.pm lib/PPIx/Regexp/Token/Whitespace.pm lib/PPIx/Regexp/Tokenizer.pm lib/PPIx/Regexp/Util.pm LICENSES/Artistic LICENSES/Copying Makefile.PL MANIFEST META.json META.yml README t/basic.t t/fuzz.t t/locations.t t/parse.t t/unit-adhoc.t t/unit.t t/version.t t/xplain.t xt/author/carp_not.t xt/author/changes.t xt/author/critic.t xt/author/executable.t xt/author/kwalitee.t xt/author/manifest.t xt/author/minimum_perl.t xt/author/perlcriticrc xt/author/pod.t xt/author/pod_coverage.t xt/author/pod_links.t xt/author/pod_spelling.t xt/author/prereq.t xt/author/unicode_short_charclass.t xt/author/unit_left_curly.t PPIx-Regexp-0.082/META.json000444000765000024 2535214151156043 14463 0ustar00tomstaff000000000000{ "abstract" : "Parse regular expressions", "author" : [ "Thomas R. Wyant, III F" ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4231", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "PPIx-Regexp", "no_index" : { "directory" : [ "eg", "inc", "t", "tools", "xt" ] }, "prereqs" : { "build" : { "requires" : { "Test::More" : "0.88", "charnames" : "0", "lib" : "0" } }, "configure" : { "requires" : { "Module::Build" : "0.42", "lib" : "0", "strict" : "0", "warnings" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "Encode" : "0", "Exporter" : "0", "List::Util" : "0", "PPI::Document" : "1.117", "PPI::Dumper" : "1.117", "Scalar::Util" : "0", "Task::Weaken" : "0", "Text::Tabs" : "0", "base" : "0", "constant" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } } }, "provides" : { "PPIx::Regexp" : { "file" : "lib/PPIx/Regexp.pm", "version" : "0.082" }, "PPIx::Regexp::Constant" : { "file" : "lib/PPIx/Regexp/Constant.pm", "version" : "0.082" }, "PPIx::Regexp::Dumper" : { "file" : "lib/PPIx/Regexp/Dumper.pm", "version" : "0.082" }, "PPIx::Regexp::Element" : { "file" : "lib/PPIx/Regexp/Element.pm", "version" : "0.082" }, "PPIx::Regexp::Lexer" : { "file" : "lib/PPIx/Regexp/Lexer.pm", "version" : "0.082" }, "PPIx::Regexp::Node" : { "file" : "lib/PPIx/Regexp/Node.pm", "version" : "0.082" }, "PPIx::Regexp::Node::Range" : { "file" : "lib/PPIx/Regexp/Node/Range.pm", "version" : "0.082" }, "PPIx::Regexp::Node::Unknown" : { "file" : "lib/PPIx/Regexp/Node/Unknown.pm", "version" : "0.082" }, "PPIx::Regexp::Structure" : { "file" : "lib/PPIx/Regexp/Structure.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Assertion" : { "file" : "lib/PPIx/Regexp/Structure/Assertion.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Atomic_Script_Run" : { "file" : "lib/PPIx/Regexp/Structure/Atomic_Script_Run.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::BranchReset" : { "file" : "lib/PPIx/Regexp/Structure/BranchReset.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Capture" : { "file" : "lib/PPIx/Regexp/Structure/Capture.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::CharClass" : { "file" : "lib/PPIx/Regexp/Structure/CharClass.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Code" : { "file" : "lib/PPIx/Regexp/Structure/Code.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Main" : { "file" : "lib/PPIx/Regexp/Structure/Main.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Modifier" : { "file" : "lib/PPIx/Regexp/Structure/Modifier.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::NamedCapture" : { "file" : "lib/PPIx/Regexp/Structure/NamedCapture.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Quantifier" : { "file" : "lib/PPIx/Regexp/Structure/Quantifier.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::RegexSet" : { "file" : "lib/PPIx/Regexp/Structure/RegexSet.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Regexp" : { "file" : "lib/PPIx/Regexp/Structure/Regexp.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Replacement" : { "file" : "lib/PPIx/Regexp/Structure/Replacement.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Script_Run" : { "file" : "lib/PPIx/Regexp/Structure/Script_Run.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Subexpression" : { "file" : "lib/PPIx/Regexp/Structure/Subexpression.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Switch" : { "file" : "lib/PPIx/Regexp/Structure/Switch.pm", "version" : "0.082" }, "PPIx::Regexp::Structure::Unknown" : { "file" : "lib/PPIx/Regexp/Structure/Unknown.pm", "version" : "0.082" }, "PPIx::Regexp::Support" : { "file" : "lib/PPIx/Regexp/Support.pm", "version" : "0.082" }, "PPIx::Regexp::Token" : { "file" : "lib/PPIx/Regexp/Token.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Assertion" : { "file" : "lib/PPIx/Regexp/Token/Assertion.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Backreference" : { "file" : "lib/PPIx/Regexp/Token/Backreference.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Backtrack" : { "file" : "lib/PPIx/Regexp/Token/Backtrack.pm", "version" : "0.082" }, "PPIx::Regexp::Token::CharClass" : { "file" : "lib/PPIx/Regexp/Token/CharClass.pm", "version" : "0.082" }, "PPIx::Regexp::Token::CharClass::POSIX" : { "file" : "lib/PPIx/Regexp/Token/CharClass/POSIX.pm", "version" : "0.082" }, "PPIx::Regexp::Token::CharClass::POSIX::Unknown" : { "file" : "lib/PPIx/Regexp/Token/CharClass/POSIX/Unknown.pm", "version" : "0.082" }, "PPIx::Regexp::Token::CharClass::Simple" : { "file" : "lib/PPIx/Regexp/Token/CharClass/Simple.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Code" : { "file" : "lib/PPIx/Regexp/Token/Code.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Comment" : { "file" : "lib/PPIx/Regexp/Token/Comment.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Condition" : { "file" : "lib/PPIx/Regexp/Token/Condition.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Control" : { "file" : "lib/PPIx/Regexp/Token/Control.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Delimiter" : { "file" : "lib/PPIx/Regexp/Token/Delimiter.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Greediness" : { "file" : "lib/PPIx/Regexp/Token/Greediness.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType" : { "file" : "lib/PPIx/Regexp/Token/GroupType.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Assertion" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Assertion.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Atomic_Script_Run" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Atomic_Script_Run.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::BranchReset" : { "file" : "lib/PPIx/Regexp/Token/GroupType/BranchReset.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Code" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Code.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Modifier" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Modifier.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::NamedCapture" : { "file" : "lib/PPIx/Regexp/Token/GroupType/NamedCapture.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Script_Run" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Script_Run.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Subexpression" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Subexpression.pm", "version" : "0.082" }, "PPIx::Regexp::Token::GroupType::Switch" : { "file" : "lib/PPIx/Regexp/Token/GroupType/Switch.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Interpolation" : { "file" : "lib/PPIx/Regexp/Token/Interpolation.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Literal" : { "file" : "lib/PPIx/Regexp/Token/Literal.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Modifier" : { "file" : "lib/PPIx/Regexp/Token/Modifier.pm", "version" : "0.082" }, "PPIx::Regexp::Token::NoOp" : { "file" : "lib/PPIx/Regexp/Token/NoOp.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Operator" : { "file" : "lib/PPIx/Regexp/Token/Operator.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Quantifier" : { "file" : "lib/PPIx/Regexp/Token/Quantifier.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Recursion" : { "file" : "lib/PPIx/Regexp/Token/Recursion.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Reference" : { "file" : "lib/PPIx/Regexp/Token/Reference.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Structure" : { "file" : "lib/PPIx/Regexp/Token/Structure.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Unknown" : { "file" : "lib/PPIx/Regexp/Token/Unknown.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Unmatched" : { "file" : "lib/PPIx/Regexp/Token/Unmatched.pm", "version" : "0.082" }, "PPIx::Regexp::Token::Whitespace" : { "file" : "lib/PPIx/Regexp/Token/Whitespace.pm", "version" : "0.082" }, "PPIx::Regexp::Tokenizer" : { "file" : "lib/PPIx/Regexp/Tokenizer.pm", "version" : "0.082" }, "PPIx::Regexp::Util" : { "file" : "lib/PPIx/Regexp/Util.pm", "version" : "0.082" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "wyant@cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=PPIx-Regexp" }, "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "git://github.com/trwyant/perl-PPIx-Regexp.git", "web" : "https://github.com/trwyant/perl-PPIx-Regexp" } }, "version" : "0.082", "x_serialization_backend" : "JSON::PP version 4.06" } PPIx-Regexp-0.082/META.yml000444000765000024 1705114151156043 14310 0ustar00tomstaff000000000000--- abstract: 'Parse regular expressions' author: - 'Thomas R. Wyant, III F' build_requires: Test::More: '0.88' charnames: '0' lib: '0' configure_requires: Module::Build: '0.42' lib: '0' strict: '0' warnings: '0' dynamic_config: 1 generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: PPIx-Regexp no_index: directory: - eg - inc - t - tools - xt provides: PPIx::Regexp: file: lib/PPIx/Regexp.pm version: '0.082' PPIx::Regexp::Constant: file: lib/PPIx/Regexp/Constant.pm version: '0.082' PPIx::Regexp::Dumper: file: lib/PPIx/Regexp/Dumper.pm version: '0.082' PPIx::Regexp::Element: file: lib/PPIx/Regexp/Element.pm version: '0.082' PPIx::Regexp::Lexer: file: lib/PPIx/Regexp/Lexer.pm version: '0.082' PPIx::Regexp::Node: file: lib/PPIx/Regexp/Node.pm version: '0.082' PPIx::Regexp::Node::Range: file: lib/PPIx/Regexp/Node/Range.pm version: '0.082' PPIx::Regexp::Node::Unknown: file: lib/PPIx/Regexp/Node/Unknown.pm version: '0.082' PPIx::Regexp::Structure: file: lib/PPIx/Regexp/Structure.pm version: '0.082' PPIx::Regexp::Structure::Assertion: file: lib/PPIx/Regexp/Structure/Assertion.pm version: '0.082' PPIx::Regexp::Structure::Atomic_Script_Run: file: lib/PPIx/Regexp/Structure/Atomic_Script_Run.pm version: '0.082' PPIx::Regexp::Structure::BranchReset: file: lib/PPIx/Regexp/Structure/BranchReset.pm version: '0.082' PPIx::Regexp::Structure::Capture: file: lib/PPIx/Regexp/Structure/Capture.pm version: '0.082' PPIx::Regexp::Structure::CharClass: file: lib/PPIx/Regexp/Structure/CharClass.pm version: '0.082' PPIx::Regexp::Structure::Code: file: lib/PPIx/Regexp/Structure/Code.pm version: '0.082' PPIx::Regexp::Structure::Main: file: lib/PPIx/Regexp/Structure/Main.pm version: '0.082' PPIx::Regexp::Structure::Modifier: file: lib/PPIx/Regexp/Structure/Modifier.pm version: '0.082' PPIx::Regexp::Structure::NamedCapture: file: lib/PPIx/Regexp/Structure/NamedCapture.pm version: '0.082' PPIx::Regexp::Structure::Quantifier: file: lib/PPIx/Regexp/Structure/Quantifier.pm version: '0.082' PPIx::Regexp::Structure::RegexSet: file: lib/PPIx/Regexp/Structure/RegexSet.pm version: '0.082' PPIx::Regexp::Structure::Regexp: file: lib/PPIx/Regexp/Structure/Regexp.pm version: '0.082' PPIx::Regexp::Structure::Replacement: file: lib/PPIx/Regexp/Structure/Replacement.pm version: '0.082' PPIx::Regexp::Structure::Script_Run: file: lib/PPIx/Regexp/Structure/Script_Run.pm version: '0.082' PPIx::Regexp::Structure::Subexpression: file: lib/PPIx/Regexp/Structure/Subexpression.pm version: '0.082' PPIx::Regexp::Structure::Switch: file: lib/PPIx/Regexp/Structure/Switch.pm version: '0.082' PPIx::Regexp::Structure::Unknown: file: lib/PPIx/Regexp/Structure/Unknown.pm version: '0.082' PPIx::Regexp::Support: file: lib/PPIx/Regexp/Support.pm version: '0.082' PPIx::Regexp::Token: file: lib/PPIx/Regexp/Token.pm version: '0.082' PPIx::Regexp::Token::Assertion: file: lib/PPIx/Regexp/Token/Assertion.pm version: '0.082' PPIx::Regexp::Token::Backreference: file: lib/PPIx/Regexp/Token/Backreference.pm version: '0.082' PPIx::Regexp::Token::Backtrack: file: lib/PPIx/Regexp/Token/Backtrack.pm version: '0.082' PPIx::Regexp::Token::CharClass: file: lib/PPIx/Regexp/Token/CharClass.pm version: '0.082' PPIx::Regexp::Token::CharClass::POSIX: file: lib/PPIx/Regexp/Token/CharClass/POSIX.pm version: '0.082' PPIx::Regexp::Token::CharClass::POSIX::Unknown: file: lib/PPIx/Regexp/Token/CharClass/POSIX/Unknown.pm version: '0.082' PPIx::Regexp::Token::CharClass::Simple: file: lib/PPIx/Regexp/Token/CharClass/Simple.pm version: '0.082' PPIx::Regexp::Token::Code: file: lib/PPIx/Regexp/Token/Code.pm version: '0.082' PPIx::Regexp::Token::Comment: file: lib/PPIx/Regexp/Token/Comment.pm version: '0.082' PPIx::Regexp::Token::Condition: file: lib/PPIx/Regexp/Token/Condition.pm version: '0.082' PPIx::Regexp::Token::Control: file: lib/PPIx/Regexp/Token/Control.pm version: '0.082' PPIx::Regexp::Token::Delimiter: file: lib/PPIx/Regexp/Token/Delimiter.pm version: '0.082' PPIx::Regexp::Token::Greediness: file: lib/PPIx/Regexp/Token/Greediness.pm version: '0.082' PPIx::Regexp::Token::GroupType: file: lib/PPIx/Regexp/Token/GroupType.pm version: '0.082' PPIx::Regexp::Token::GroupType::Assertion: file: lib/PPIx/Regexp/Token/GroupType/Assertion.pm version: '0.082' PPIx::Regexp::Token::GroupType::Atomic_Script_Run: file: lib/PPIx/Regexp/Token/GroupType/Atomic_Script_Run.pm version: '0.082' PPIx::Regexp::Token::GroupType::BranchReset: file: lib/PPIx/Regexp/Token/GroupType/BranchReset.pm version: '0.082' PPIx::Regexp::Token::GroupType::Code: file: lib/PPIx/Regexp/Token/GroupType/Code.pm version: '0.082' PPIx::Regexp::Token::GroupType::Modifier: file: lib/PPIx/Regexp/Token/GroupType/Modifier.pm version: '0.082' PPIx::Regexp::Token::GroupType::NamedCapture: file: lib/PPIx/Regexp/Token/GroupType/NamedCapture.pm version: '0.082' PPIx::Regexp::Token::GroupType::Script_Run: file: lib/PPIx/Regexp/Token/GroupType/Script_Run.pm version: '0.082' PPIx::Regexp::Token::GroupType::Subexpression: file: lib/PPIx/Regexp/Token/GroupType/Subexpression.pm version: '0.082' PPIx::Regexp::Token::GroupType::Switch: file: lib/PPIx/Regexp/Token/GroupType/Switch.pm version: '0.082' PPIx::Regexp::Token::Interpolation: file: lib/PPIx/Regexp/Token/Interpolation.pm version: '0.082' PPIx::Regexp::Token::Literal: file: lib/PPIx/Regexp/Token/Literal.pm version: '0.082' PPIx::Regexp::Token::Modifier: file: lib/PPIx/Regexp/Token/Modifier.pm version: '0.082' PPIx::Regexp::Token::NoOp: file: lib/PPIx/Regexp/Token/NoOp.pm version: '0.082' PPIx::Regexp::Token::Operator: file: lib/PPIx/Regexp/Token/Operator.pm version: '0.082' PPIx::Regexp::Token::Quantifier: file: lib/PPIx/Regexp/Token/Quantifier.pm version: '0.082' PPIx::Regexp::Token::Recursion: file: lib/PPIx/Regexp/Token/Recursion.pm version: '0.082' PPIx::Regexp::Token::Reference: file: lib/PPIx/Regexp/Token/Reference.pm version: '0.082' PPIx::Regexp::Token::Structure: file: lib/PPIx/Regexp/Token/Structure.pm version: '0.082' PPIx::Regexp::Token::Unknown: file: lib/PPIx/Regexp/Token/Unknown.pm version: '0.082' PPIx::Regexp::Token::Unmatched: file: lib/PPIx/Regexp/Token/Unmatched.pm version: '0.082' PPIx::Regexp::Token::Whitespace: file: lib/PPIx/Regexp/Token/Whitespace.pm version: '0.082' PPIx::Regexp::Tokenizer: file: lib/PPIx/Regexp/Tokenizer.pm version: '0.082' PPIx::Regexp::Util: file: lib/PPIx/Regexp/Util.pm version: '0.082' requires: Carp: '0' Encode: '0' Exporter: '0' List::Util: '0' PPI::Document: '1.117' PPI::Dumper: '1.117' Scalar::Util: '0' Task::Weaken: '0' Text::Tabs: '0' base: '0' constant: '0' perl: '5.006' strict: '0' warnings: '0' resources: bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=PPIx-Regexp license: http://dev.perl.org/licenses/ repository: git://github.com/trwyant/perl-PPIx-Regexp.git version: '0.082' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' PPIx-Regexp-0.082/Makefile.PL000444000765000024 375314151156043 14775 0ustar00tomstaff000000000000use strict; use warnings; use lib qw{ inc }; use ExtUtils::MakeMaker; use My::Module::Meta; (my $mmv = ExtUtils::MakeMaker->VERSION) =~ s/_//g; my $meta = My::Module::Meta->new(); my %args = ( ABSTRACT => $meta->abstract(), AUTHOR => $meta->author(), DISTNAME => $meta->dist_name(), EXE_FILES => $meta->script_files(), NAME => $meta->module_name(), PREREQ_PM => $meta->requires(), PL_FILES => {}, # Prevent old MakeMaker from running Build.PL realclean => { FILES => join( ' ', @{ $meta->add_to_cleanup() } ), }, VERSION_FROM => $meta->version_from(), ); $mmv >= 6.31 and $args{LICENSE} = $meta->license(); if ( $mmv >= 6.4501 ) { $args{META_ADD} = { no_index => $meta->no_index(), $meta->provides(), }; $args{META_MERGE} = $meta->meta_merge(); } $mmv >= 6.4701 and $args{MIN_PERL_VERSION} = $meta->requires_perl(); if ( $mmv >= 6.52 ) { $args{BUILD_REQUIRES} = $meta->build_requires(); $args{CONFIGURE_REQUIRES} = $meta->configure_requires(); } elsif ( $mmv >= 6.5501 ) { $args{BUILD_REQUIRES} = $meta->build_requires(); $args{META_MERGE}{configure_requires} = $meta->configure_requires(); } elsif ( $mmv >= 6.4501 ) { $args{META_MERGE}{build_requires} = $meta->build_requires(); $args{META_MERGE}{configure_requires} = $meta->configure_requires(); } else { foreach my $method ( qw{ configure_requires build_requires } ) { my $req = $meta->$method(); foreach my $key ( keys %{ $req } ) { exists $args{PREREQ_PM}{$key} or $args{PREREQ_PM}{$key} = $req->{$key}; } } } WriteMakefile( %args ); sub MY::postamble { my ( $self, @args ) = @_; my $authortest = $self->test_via_harness( '$(FULLPERLRUN)', '$(AUTHORTEST_FILES)' ); $authortest =~ s/ \s+ \z //smx; $authortest =~ s/ \A \s+ //smx; chomp $authortest; return <<"EOD"; AUTHORTEST_FILES = t/*.t xt/author/*.t authortest :: pure_all AUTHOR_TESTING=1 $authortest testcover :: pure_all cover -test EOD } # ex: set textwidth=72 : PPIx-Regexp-0.082/README000444000765000024 321414151156043 13673 0ustar00tomstaff000000000000PPIx-Regexp is Copyright (C) 2009-2021 by Thomas R. Wyant, III DESCRIPTION This package parses regular expressions as they appear in Perl scripts, generating a structure similar to the structure generated by PPI when it parses a Perl script, and navigable in much the same way. The PPIx::Regexp object is instantiated using either new() or new_from_cache(). Either way, you must pass it a regular expression, either as a string or as one of the three relevant PPI objects: PPI::Token::StringLike::Regexp, PPI::Token::Regexp::Match, or PPI::Token::Regexp::Substitute. In the case of new_from_cache(), only one PPIx::Regexp object will be generated from a given PPI object; subsequent calls with the same PPI object will return the same PPIx::Regexp object. See the eg directory for samples. INSTALLATION This module is installable by either of the two usual incantations: tar -xzf PPIx-Regexp-9.999.tar.gz cd PPIx-Regexp-9.999 perl Makefile.PL make make test sudo make install or tar -xzf PPIx-Regexp-9.999.tar.gz cd PPIx-Regexp-9.999 perl Build.PL ./Build ./Build test sudo ./Build install Of course, since it is pure Perl, in desperation you can simply drop the files from the lib directory of the distribution into the appropriate @INC directory. LICENSING INFORMATION This package is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. PPIx-Regexp-0.082/LICENSES000755000765000024 014151156043 14063 5ustar00tomstaff000000000000PPIx-Regexp-0.082/LICENSES/Artistic000444000765000024 1373714151156043 15760 0ustar00tomstaff000000000000 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End PPIx-Regexp-0.082/LICENSES/Copying000444000765000024 3053014151156043 15574 0ustar00tomstaff000000000000 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! PPIx-Regexp-0.082/eg000755000765000024 014151156043 13251 5ustar00tomstaff000000000000PPIx-Regexp-0.082/eg/README000444000765000024 271214151156043 14270 0ustar00tomstaff000000000000This directory contains example scripts using PPIx::Regexp. Specifically: README - is this file. find-variable-length-lookarounds - is a script that examines Perl files looking for variable-length lookarounds. interpolated - is a script that takes as its arguments a list of files and/or directories, and lists all Perl regular expressions which interpolate. If the -once option is asserted, only those expressions which interpolate and do not have the /o modifier are listed. preaccepts - is a script that takes as its arguments a Perl regular expression and one or more Perl version numbers. For each Perl version number, it displays whether that version of Perl considers the regular expression to be valid. predump - is a script that takes as its argument a regular expression, and dumps the parse tree. Various command options are available to control the dump. The -help option gets you complete documentation of the script. prenav - is a script that takes as its single argument a regular expression. The user can then interactively navigate around the parse tree. The 'help' command gives help. preslurp - is a script that makes files into PPI::Document objects, then extracts the regular expression tokens from the documents and dumps them. Various command options are available to control the dump. The -help option gets you complete documentation of the script. # ex: set textwidth=72 autoindent : PPIx-Regexp-0.082/eg/find-variable-length-lookarounds000555000765000024 1261714151156043 21703 0ustar00tomstaff000000000000#!/usr/bin/env perl use 5.006; use strict; use warnings; use File::Find; use File::Spec; use Getopt::Long 2.33 qw{ :config auto_version }; use Pod::Usage; use PPI::Document; use PPIx::Regexp; our $VERSION = '0.082'; my %opt; my %ignore_dir = map { $_ => 1 } qw{ .bzr .cabal-sandbox .cdv .git .hg .metadata .pc .svn CMakeFiles CVS RCS SCCS _MTN _build _darcs _sgbak autom4te.cache blib cover_db node_modules ~.dep ~.dot ~.nib ~.plst }; GetOptions( \%opt, qw{ break! files_with_matches|files-with-matches|l! }, 'ignore_directory|ignore-directory=s' => sub { $ignore_dir{$_[1]} = 1; }, 'no_ignore_directory|no-ignore-directory=s' => sub { delete $ignore_dir{$_[1]}; }, dump => sub { print " --ignore-directory=$_\n" for sort keys %ignore_dir; exit; }, help => sub { pod2usage( { -verbose => 2 } ) }, ) or pod2usage( { -verbose => 0 } ); @ARGV or @ARGV = ( File::Spec->curdir() ); defined $opt{break} or $opt{break} = ! $opt{files_with_matches}; $opt{_break} = $opt{break} ? "\n" : ''; find( \&handler, @ARGV ); sub handler { -d $_ and do { $File::Find::prune = $ignore_dir{$_}; return; }; is_perl() or return; my $doc = PPI::Document->new( $_ ) or do { warn "Unable to make PPI::Document from $File::Find::name: @{[ PPI::Document->errstr() ]}\n"; return; }; my $header; foreach my $re ( PPIx::Regexp->extract_regexps( $doc ) ) { foreach my $assertion ( @{ $re->find( 'PPIx::Regexp::Structure::Assertion' ) || [] } ) { $assertion->type()->content() =~ m/ \A [?] find( 'PPIx::Regexp::Token::Quantifier' ) or $assertion->find( 'PPIx::Regexp::Structure::Quantifier' ) or next; my $loc = $re->source()->location(); $header++ or print "$opt{_break}$File::Find::name\n"; $opt{files_with_matches} or print "$loc->[0]:@{[ $re->content() ]}\n"; last; } } } sub is_perl { -T _ or return; m/ [.] (?: pm | t | (?i: pl ) ) \z /smx and return 1; open my $fh, '<', $_ or return; defined( local $_ = <$fh> ) or return; close $fh; return m/ \A \#! .* perl /smx; } __END__ =head1 TITLE find-variable-length-lookarounds - Find regular expressions that use variable-length lookarounds =head1 SYNOPSIS find-variable-length-lookarounds find-variable-length-lookarounds --help find-variable-length-lookarounds --version =head1 OPTIONS Option names have been chosen to be compatible (or at least close) to C. =head2 --break Asserting this Boolean option causes a blank line to be inserted before the file name in the output. The default is the inverse of the value of L<--files-with-matches|/--files-with-matches>. =head2 --dump This option causes the configuration to be dumped. The script then exits. The configuration consists of the list of ignored directories in effect as of the time the C<--dump> option was encountered. =head2 --files-with-matches Asserting this option suppresses the listing of individual regular expressions. The default is C<--no-files-with-matches>, which causes both file name and regular expressions to be listed. =head2 --help This option displays the documentation for this script. The script then exits. =head2 --ignore-directory --ignore-directory=fubar This option adds a directory to the list of directories to be ignored. It may be specified multiple times. The initial list was cribbed from C. =head2 -l This Boolean option is a synonym for L<--files-with-matches|/--files-with-matches>. =head2 --no-ignore-directory --no-ignore-directory=fubar This option removes a directory from the list of directories to be ignored. It is not an error to remove a directory that was not on the list. =head2 --version This option displays the version of this script. The script then exits. =head1 DETAILS This Perl script reads Perl files, and finds and displays regular expressions that contain variable-length lookarounds, either lookaheads or lookbehinds. These are defined as lookarounds that contain at least one quantifier. Note that this definition does B catch lookarounds that are variable-length due to current case-folding rules (which, among other things, require ligatures to match non-ligatures, and a German sharp s to match a double s). The files to analyze are specified on the command line. Directories are traversed recursively, with directories not likely to be of interest being skipped. Only files that appear to be Perl are actually analyzed. These are text files whose names end in F<.pm>, F<.t>, F<.pl>, or F<.PL>, or begin with a shebang line containing C<'perl'>. If no files are specified on the command line, the default directory is analyzed. For each file containing at least one variable-length lookaround, the name of the file is listed. Each regular expression containing a variable-length lookaround is listed, preceded by its line number in the file. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2019-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/eg/interpolated000555000765000024 545114151156043 16033 0ustar00tomstaff000000000000#!/usr/local/bin/perl use 5.006; use strict; use warnings; use File::Find; use File::Spec; use Getopt::Long; use PPI; use PPIx::Regexp; my %opt; GetOptions( \%opt, qw{ once! } ) or die; find( \&interpolated, @ARGV ? @ARGV : ( File::Spec->curdir() ) ); sub interpolated { -d $_ and return prune(); is_perl( $_ ) or return; my $named; my $doc = PPI::Document->new( $_ ); foreach my $class ( qw{ PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute PPI::Token::QuoteLike::Regexp } ) { foreach my $elem ( @{ $doc->find( $class ) || [] } ) { my $re = PPIx::Regexp->new( $elem ); $re->regular_expression()->find_first( 'PPIx::Regexp::Token::Interpolation' ) or next; $opt{once} and $re->modifier()->asserts( 'o' ) and next; $named++ or print $File::Find::name, "\n"; print ' ', $re->content(), "\n"; } } } sub is_perl { my ( $fn ) = @_; -T $fn or return; $fn =~ m/ [.] pm \z /smx and return 1; $fn =~ m/ [.] pl \z /smxi and return 1; open ( my $fh, '<', $fn ) or return; local $_ = <$fh>; close $fh; defined $_ or return; m/ \A [#] ! .*? perl /smx and return 1; m/ \A [#] ! /smx and return; $fn =~ m/ [.] t \z /smx and return 1; return; } { my %pruned; BEGIN { %pruned = map { $_ => 1 } qw{ blib }; } sub prune { '.' eq $_ and return; $pruned{$_} or m/ \A [.] /smx or return; $File::Find::prune = 1; return; } } __END__ =head1 TITLE interpolated - Find all interpolated regular expressions =head1 SYNOPSIS interpolated interpolated lib interpolated -once =head1 OPTIONS =over =item -once If this option is asserted, only interpolated regular expressions B the C qualifier are found. =back =head1 DETAILS This script searches files or directories for interpolated regular expressions. These are considered to be matches or C expressions containing interpolations, or substitutions containing interpolations in their regular expressions. Interpolation in the replacement portion of the substitution are not considered. The names of any files containing such regular expressions are listed, along with the statements themselves. If C<-once> is asserted, only regular expressions without the C modifier are listed. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2010-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/eg/preaccepts000555000765000024 351714151156043 15473 0ustar00tomstaff000000000000#!/usr/bin/env perl use 5.006; use strict; use warnings; use Getopt::Long 2.33 qw{ :config auto_version }; use Pod::Usage; use PPIx::Regexp; our $VERSION = '0.082'; my %opt; GetOptions( \%opt, help => sub { pod2usage( { -verbose => 2 } ) }, ) and @ARGV > 1 or pod2usage( { -verbose => 0 } ); my $pre = PPIx::Regexp->new( my $regexp = shift @ARGV ); foreach my $perl ( @ARGV ) { print $regexp, ( $pre->accepts_perl( $perl ) ? ' is' : ' is not' ), " accepted by $perl\n"; } __END__ =head1 TITLE preaccepts - See whether specified versions of Perl accept a given regular expression =head1 SYNOPSIS preaccepts '/x{' 5.025 5.025001 5.026 5.027 5.027001 preaccepts -help preaccepts -version =head1 OPTIONS =head2 -help This option displays the documentation for this script. The script then exits. =head2 -version This option displays the version of this script. The script then exits. =head1 DETAILS This Perl script accepts as its first argument a Perl regular expression, and as its second and subsequent arguments Perl verions numbers. It displays to standard out whether or not each given version of Perl considers the specified regular expression to be correct. The heavy lifting for this is done by the L L method. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2017-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/eg/predump000555000765000024 2121514151156043 15031 0ustar00tomstaff000000000000#!/usr/local/bin/perl use 5.006; use strict; use warnings; BEGIN { if ( '5.008' le $] ) { # Have to prevent Perl from parsing 'open' as 'CORE::open'. require 'open.pm'; 'open'->import( qw{ :std :encoding(utf-8) } ); } } use Getopt::Long 2.33 qw{ :config auto_version }; use Pod::Usage; use PPI::Document; use PPIx::Regexp::Dumper; use Scalar::Util qw{ refaddr }; use vars qw{ $VERSION }; $VERSION = '0.082'; my %opt = ( default_modifiers => [], verbose => 0, ); GetOptions( \%opt, help => sub { pod2usage ( { -exitval => 0, -verbose => 2, -output => \*STDOUT, } ) }, qw{ default_modifiers|default-modifiers=s@ encoding=s explain! files! indent=i locations! margin=i objectify! ordinal! perl_version|perl-version! ppi! short! significant! strict! test! tokens! trace+ unescape! verbose+ } ) and @ARGV or pod2usage( { -exitval => 2, -verbose => 1, -output => \*STDERR, } ); $opt{default_modifiers} = [ map { split qr{ \s* , \s* }smx } @{ $opt{default_modifiers} } ]; foreach my $re ( process_args( \%opt, @ARGV ) ) { if ( ! $opt{test} ) { my @output = ( "\n$re" ); @{ $opt{default_modifiers} } and push @output, q{default_modifiers => '} . join( ',', @{ $opt{default_modifiers} } ) . q{'}; print join( "\t", @output ), "\n"; @output = item_info( $re ) and print join( "\t", @output ), "\n"; } PPIx::Regexp::Dumper->new( $re, %opt )->print(); } { my @docs; # Have to save reference my %file; sub process_args { my ( $opt, @args ) = @_; my @rslt; foreach my $datum ( @args ) { if ( $opt->{files} ) { my $doc = PPI::Document->new( $datum, readonly => 1 ) or die "Can not make PPI::Document from file '$datum'\n"; push @docs, $doc; push @rslt, extract_res( $doc, $opt ); $file{ refaddr( $doc ) } = { name => $datum, }; } else { $opt->{unescape} and $datum =~ s/ \\\\ /\\/smxg; if ( $opt->{objectify} ) { my $doc = PPI::Document->new( \$datum ) or die "Can not make PPI::Document from '$datum'\n"; push @docs, $doc; push @rslt, extract_res( $doc, $opt ); } else { push @rslt, $datum; } } } delete $opt->{files}; delete $opt->{objectify}; delete $opt->{unescape}; return @rslt; } sub item_info { my ( $obj ) = @_; ref $obj or return; eval { $obj->isa( 'PPI::Element' ); } or return; my $doc = $obj->document() or return; my $info = $file{ refaddr $doc } or return; return wantarray ? ( $info->{name}, @{ $obj->location() || [] }[0, 2] ) : $info->{name}; } } { my ( %classes, @regex, @string ); BEGIN { @regex = qw{ PPI::Token::QuoteLike::Regexp PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute }; @string = qw{ PPI::Token::Quote PPI::Token::QuoteLike::Command PPI::Token::QuoteLike::BackTick PPI::Token::HereDoc }; %classes = ( guess => [ @regex, @string ], regex => \@regex, string => \@string, ); } sub extract_res { my ( $doc, $opt ) = @_; my $parse = $opt->{parse} || 'regex'; 'regex' eq $parse and return PPIx::Regexp->extract_regexps( $doc ); # TODO get rid of this whole mess in favor of the above line # once the string functionality goes away. return ( map { @{ $doc->find( $_ ) || [] } } @{ $classes{$parse} || $classes{regex} } ); } } __END__ =head1 NAME predump - Dump a regular expression =head1 SYNOPSIS predump 'qr{foo}smx' predump --ordinal 'm/foo/x' You can use predump --help for full documentation on usage. =head1 DESCRIPTION This Perl script parses the regular expression given on its command line and dumps the results of the parse to standard out. Options are accepted with leading single dashes as well as double dashes. The following options are recognized: =over =item --default-modifiers text This option specifies default modifiers for the regular expression. You can specify more than one, either as a comma-separated list or by specifying the option multiple times, or both. It is simply passed through to L<< PPIx::Regexp->new()|PPIx::Regexp/new >>. This option can also be expressed as C<--default_modifiers>. =item --encoding name This option specifies the encoding of the regular expression. It is simply passed through to L<< PPIx::Regexp->new()|PPIx::Regexp/new >>. =item --files If true, this option specifies that the arguments are files whose regular expressions are to be analyzed. If this options is asserted, C<--objectify> and C<--unescape> are ignored. =item --help This option displays the documentation for this script. The script then exits. =item --indent number This option specifies the number of spaces to indent each level of the parse hierarchy. It is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --margin number This option specifies the width of the left margin of the dump output. It is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --objectify If true, this option specifies that the arguments should be made into L objects before being passed to PPIx::Regexp. This option is ignored if C<--files> is asserted. =item --ordinal If true, this option specifies that the ordinal value of all L objects be displayed as part of the dump. The default is false. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --perl-version If true, this option specifies that the dump include the perl version applicable to each dumped item. The default is false. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. This option can also be expressed as C<--perl_version>. =item --significant If true, this option specifies that the dump include only significant syntax elements. That is, no comments or non-significant white space. The default is false. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --short If true, this option specifies that class names in the dump will have the leading C<'PPIx::Regexp::'> removed. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --test If true, this option specifies that the dump take the form of a predefined set of tests be generated for the regular expression. This option is unsupported in the sense that the author makes no commitment to what it will do, and reserves the right to change it without notice. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --tokens If true, this option specifies that only tokenization be done on the regular expression, and the output tokens dumped to standard out. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --trace If true, this option specifies the generation of trace output from the parse. It is unsupported in the sense that the author makes no commitment to what it will do, and reserves the right to change it without notice. This is simply passed through to L<< PPIx::Regexp->new()|PPIx::Regexp/new >>. =item --unescape If true, this option causes the argument to be unescaped before processing. You would use it if the argument is a Perl single-quotish string, since Perl's single-quoted syntax differs from that of the usual Unix shell. This option is ignored if C<-files> is asserted. =item --verbose If true, this option causes more information to be dumped about each object produced by the parse. It is unsupported in the sense that the author makes no commitment to what it will do, and reserves the right to change it without notice. This is simply passed through to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >>. =item --version This option displays the version of this script. The script then exits. =back =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/eg/prenav000555000765000024 1530014151156043 14646 0ustar00tomstaff000000000000#!/usr/local/bin/perl use strict; use warnings; use Pod::Usage; use PPIx::Regexp; use PPIx::Regexp::Constant qw{ ARRAY_REF SCALAR_REF }; use PPIx::Regexp::Dumper; use PPIx::Regexp::Util qw{ __instance }; use Term::ReadLine; @ARGV or die "Need an argument.\n"; my $re = PPIx::Regexp->new( $ARGV[0] ) or die PPIx::Regexp->errmsg(); my $obj = $re; my $tr = Term::ReadLine->new( 'Navigate a regular expression' ); my %internal = ( capture_names => sub { defined $obj or return; return join( ', ', map { "'$_'" } $obj->capture_names() ); }, delimiters => sub { defined $obj or return; return join( ', ', map { "'$_'" } $obj->delimiters() ); }, dump => sub { my @args; defined $_[0] and @args = split qr{ \s+ }smx, $_[0]; return PPIx::Regexp::Dumper->new( $obj, @args )->string(); }, help => sub { my ( $arg ) = @_; my @extra; if ( defined $arg ) { $arg =~ m/ \A [[:alpha:]_] \w* (?: :: [[:alpha:]_] \w* )* \z /smx or die "Invalid module name $arg\n"; ( my $fn = "$arg.pm" ) =~ s< :: >smxg; push @extra, '-input', exists $INC{$fn} ? $INC{$fn} : _find_file_in_inc( $fn, "Can not find module $arg" ); } pod2usage( -exitval => 'NOEXIT', -verbose => 2, -output => \*STDOUT, @extra, ); return \''; }, nav => sub { return _safe( $obj->nav() ); }, parse => sub { my $temp = PPIx::Regexp->new( $_[0] ) or return PPIx::Regexp->errstr(); return ( $obj = $re = $temp ); }, reset => sub { return ( $obj = $re ); }, ); sub _safe { my ( @args ) = @_; my $rslt = join ', ', map { ARRAY_REF eq ref $_ ? '[ ' . _safe( @{ $_ } ) . ' ]' : "'$_'" } @args; $rslt =~ s/ \[ \s+ \] /[]/smxg; $rslt =~ s/ ' ( \d+ ) ' /$1/smxg; $rslt =~ s/ \[ \s* ( \d+ ) \s* \] /$1/smxg; $rslt =~ s/ ' ( \w+ ) ', /$1 =>/smxg; return $rslt; } while ( defined ( my $buffer = $tr->readline( 'prenav> ' ) ) ) { $buffer =~ s/ \s+ \z //smx; $buffer or next; $buffer =~ s/ \A \s+ //smx; '#' eq substr $buffer, 0, 1 and next; my ( $method, $arg ) = split qr{\s+}smx, $buffer, 2; 'exit' eq $method and last; my $temp; eval { $temp = $internal{$method} ? $internal{$method}->( $arg ) : $obj->$method( $arg ); 1; } or do { warn $@; next; }; print _format( $temp ); __instance( $temp, 'PPIx::Regexp::Element' ) and $obj = $temp; } sub _find_file_in_inc { my ( $fn, $msg ) = @_; foreach my $dir ( @INC ) { ref $dir # Guard against code and objects in @INC and next; my $path = "$dir/$fn"; -f $path and return $path; } $msg ||= "Can not find file $fn"; $msg =~ s/ (class() . "\t" . $thing->content() . "\n"; } elsif ( ARRAY_REF eq ref $thing ) { $rslt .= _format( @{ $thing } ); } elsif ( SCALAR_REF eq ref $thing ) { $rslt .= ${ $thing }; } elsif ( defined $thing ) { $rslt .= $thing =~ m/ \n /smx ? $thing : $thing =~ m/ \A ' /smx ? "$thing\n" : $thing =~ m/ \D /smx ? "'$thing'\n" : "$thing\n"; } else { $rslt .= "undef\n"; } } return $rslt; } __END__ =head1 NAME prenav - Navigate a PPIx::Regexp parse tree =head1 SYNOPSIS prenav 's/(\w+)/\u$1/g' prenav> find_first Token::CharClass::Simple PPIx::Regexp::Token::CharClass::Simple \w prenav> dump verbose 1 PPIx::Regexp::Token::CharClass::Simple '\\w' significant can_be_quantified prenav> parent PPIx::Regexp::Structure::Capture (\w+) prenav> exit =head1 DESCRIPTION This script takes as its argument a string to be parsed as a regular expression, and prompts the user for navigation commands. A navigation command is any method that returns another element in the parse tree. Unless documented otherwise, all commands apply to the current object. Initially the current object is the L object generated by the parse. Once a navigation command is issued, the object navigated to becomes the current object. If the navigation command does not specify an object (e.g. C when the current object has fewer than 5 children) the current object remains unchanged. In addition to the navigation methods, any method that returns a scalar value can be used as a command. The value returned will be displayed. In addition to all the above, the following commands are recognized: =over =item capture_names This command wraps the L<< PPIx::Regexp->capture_names()|PPIx::Regexp/capture_names >> method, joining the results into a comma-delimited string. =item dump This command dumps the current object. Options to L<< PPIx::Regexp::Dumper->new()|PPIx::Regexp::Dumper/new >> may be specified as arguments to the command. See the L for an example. =item exit This comamnd terminates the script. =item help This command displays this documentation. If you supply a module name as an argument, the documentation for that module will be displayed. =item nav nav This command displays the method calls and arguments needed to navigate from the root of the parse tree to the current object. Yes, this is a perfectly good method, but we wrap the results of that method in some semi-nice formatting. Any arguments are ignored =item parse parse s/ ( \w+ ) foo \1 /bar/smx This command provides another regular expression to parse. If the parse succeeds, the previous regular expression is abandoned, and the new L object becomes the current object. The new regular expression is taken to be everything on the line after the whit espace after the word C. It should B be quoted. =item reset This command selects the top-level object as the current object. The C command does the same thing, but C does it by running through the parent chain, where C simply slam-dunks the retained L object. =back =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/eg/preslurp000555000765000024 1647514151156043 15245 0ustar00tomstaff000000000000#!/usr/bin/env perl use strict; use warnings; use Cwd qw{ abs_path getcwd }; use File::Find; use File::Temp; use Getopt::Long 2.33 qw{ :config auto_version }; use IO::File; use Pod::Usage; use PPI::Document; use PPIx::Regexp; use PPIx::Regexp::Dumper; use PPIx::Regexp::Tokenizer; our $VERSION = '0.082'; my %opt = ( verbose => 0, ); our $BASE = getcwd(); our @PREFIX; GetOptions( \%opt, qw{ archive! encoding=s failures! ignore=s include! ordinal! recurse! significant! tokens! verbose+ }, help => sub { pod2usage( { -verbose => 2 } ) }, ) or pod2usage( { -verbose => 0 } ); if ( $opt{archive} ) { require Archive::Any; } my @ignore; if ( $opt{ignore} ) { my $fh = IO::File->new( $opt{ignore}, '<' ) or die "Unable to open $opt{ignore}: $!\n"; while ( <$fh> ) { s/ \s+ \z //smx; $_ or next; s/ \A \s+ //smx; '#' eq substr $_, 0, 1 and next; push @ignore, qr{$_}smx; } close $fh; } my %dumper_opt = ( encoding => $opt{encoding}, margin => 4, ordinal => $opt{ordinal}, significant => $opt{significant}, verbose => $opt{verbose}, ); my $parser = $opt{tokens} ? 'PPIx::Regexp::Tokenizer' : 'PPIx::Regexp'; if ( $opt{include} ) { push @ARGV, @INC; $opt{recurse} = 1; } $opt{recurse} ||= $opt{archive}; if ( $opt{recurse} ) { @ARGV or push @ARGV, File::Spec->curdir(); find( { no_chdir => 1, wanted => sub { foreach my $re ( @ignore ) { $File::Find::name =~ $re and return; } handle_perl( $_ ) || handle_archive( $_ ); }, }, @ARGV ); } else { foreach ( @ARGV ) { -T $_ && slurp( $_ ); } } sub handle_archive { my ( $fn ) = @_; $opt{archive} or return; -f $fn or return; -T $fn and return; $fn =~ m/ [.] (?: gz | bz2 | zip | tgz | tar ) \z /smx or return; $opt{verbose} and warn "$fn\n"; my $arch = Archive::Any->new( $fn ) or return; $arch->is_naughty() and do { warn "$fn is naughty. Skipping.\n"; return; }; $arch->is_impolite() and do { warn "$fn is impolite. Skipping.\n"; return; }; my $dir = File::Temp->newdir(); eval { $arch->extract( $dir ); 1; } or do { warn "Extract from $fn failed.\n"; }; local @PREFIX = File::Spec->abs2rel( $fn ); local $BASE = $dir; find( { no_chdir => 1, wanted => sub { foreach my $re ( @ignore ) { $File::Find::name =~ $re and return; } handle_perl( $_ ) } }, $dir ); return 1; } sub handle_perl { my ( $fn ) = @_; is_perl( $fn ) or return; $opt{verbose} and warn "$fn\n"; slurp( $fn ); return 1; } sub is_perl { my ( $fn ) = @_; -T $fn or return; $fn =~ m/ [.] pm \z /smx and return 1; $fn =~ m/ [.] pl \z /smxi and return 1; open ( my $fh, '<', $fn ) or return; local $_ = <$fh>; close $fh; defined $_ or return; m/ \A [#] ! .*? perl /smx and return 1; m/ \A [#] ! /smx and return; $fn =~ m/ [.] t \z /smx and return 1; return; } sub slurp { my ( $fn ) = @_; my $doc = PPI::Document->new( $fn ) or do { warn "Unable to make a PPI::Document from $fn: ", PPI::Document->errstr; return; }; my @found; foreach my $class ( qw{ PPI::Token::QuoteLike::Regexp PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute } ) { foreach my $token ( @{ $doc->find( $class ) || [] } ) { my $display = !$opt{failures}; my @regex = ( $token ); if ( my $re = $parser->new( $token, encoding => $opt{encoding} ) ) { if ( $re->isa( 'PPIx::Regexp::Tokenizer' ) ) { push @regex, [ $re->tokens() ]; } else { push @regex, $re; } $display ||= $re->failures(); } else { $display = 1; push @regex, $token->class(), ' not handled'; } $display and push @found, \@regex; } } if ( @found ) { print "\n", join( ' ', @PREFIX, $fn ), "\n"; foreach ( @found ) { my ( $thing, $content ) = @{ $_ }; print ' ', $thing->class(), "\t", $thing->content(), "\n"; my $dmp = PPIx::Regexp::Dumper->new( $content, %dumper_opt ); $dmp->print(); } } return; } __END__ =head1 NAME preslurp - Analyze the regular expressions in a bunch of files =head1 SYNOPSIS preslurp -recurse . For full details on usage, use preslurp -help =head1 DETAILS This script makes L objects out of files it deems are Perl, then does a L dump on all L, L and L objects it finds. The following options control its use: =over =item -archive If this option is asserted, it specifies that any archive files found should be expanded and their contents searched. This option implies C<-recurse>. This option is only available if L is installed. =item -encoding name This option specifies the encoding of the files. It is passed directly to L and L. =item -failures If this option is asserted, only regular expressions with parse failures are reported. =item -help If this option is asserted, this documentation is displayed and the script exits. =item -ignore filename This option specifies the name of a file containing a list of files to ignore. The files are specified as regular expressions, listed one per line. Blank lines and lines beginning with '#' are ignored. =item -include If this option is asserted, it specifies that the contents of C<@INC> be added to the files to be checked. It also causes -recurse to be asserted. =item -ordinal If this option is asserted, it specifies that the ordinal value of any L objects be displayed in the dump. In reality, this option is simply passed to L. =item -recurse If this option is specified, this script recurses into any directories found. =item -significant If this option is asserted, only significant C objects are dumped. In reality, this option is simply passed to L. =item -tokens If this option is asserted, only L objects are dumped. In reality, this option is simply passed to L. =item -verbose If this option is asserted, you get a more verbose dump, though what that means is undocumented. In reality, this option is simply passed to L. =back =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/inc000755000765000024 014151156043 13427 5ustar00tomstaff000000000000PPIx-Regexp-0.082/inc/My000755000765000024 014151156043 14014 5ustar00tomstaff000000000000PPIx-Regexp-0.082/inc/My/Module000755000765000024 014151156043 15241 5ustar00tomstaff000000000000PPIx-Regexp-0.082/inc/My/Module/Build.pm000444000765000024 414414151156043 16776 0ustar00tomstaff000000000000package My::Module::Build; use strict; use warnings; use Module::Build; our @ISA = qw{ Module::Build }; our $VERSION = '0.082'; use Carp; sub ACTION_authortest { my ( $self ) = @_; # @args unused local $ENV{AUTHOR_TESTING} = 1; $self->depends_on( 'build' ); $self->test_files( qw{ t xt/author } ); $self->depends_on( 'test' ); return; } 1; __END__ =head1 NAME My::Module::Build - Extend Module::Build for PPIx::Regexp =head1 SYNOPSIS perl Build.PL ./Build ./Build test ./Build authortest # supplied by this module ./Build install =head1 DESCRIPTION This extension of L adds the following action to those provided by L: authortest =head1 ACTIONS This module provides the following action: =over =item authortest This action runs not only those tests which appear in the F directory, but those that appear in the F directory. The F tests are provided for information only, since some of them (notably F and F) are very sensitive to the configuration under which they run. Some of the F tests require modules that are not named as requirements. These should disable themselves if the required modules are not present. This test is sensitive to the C argument, but not to the C<--test_files> argument. =back =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/inc/My/Module/Meta.pm000444000765000024 1756214151156043 16655 0ustar00tomstaff000000000000package My::Module::Meta; use 5.006; use strict; use warnings; use Carp; sub new { my ( $class ) = @_; ref $class and $class = ref $class; my $self = { distribution => $ENV{MAKING_MODULE_DISTRIBUTION}, }; bless $self, $class; return $self; } sub abstract { return 'Parse regular expressions'; } sub add_to_cleanup { return [ qw{ cover_db } ]; } sub author { return 'Thomas R. Wyant, III F'; } sub build_requires { return +{ 'Test::More' => 0.88, # Because of done_testing(). charnames => 0, lib => 0, }; } sub configure_requires { return +{ 'lib' => 0, 'strict' => 0, 'warnings' => 0, }; } sub dist_name { return 'PPIx-Regexp'; } sub distribution { my ( $self ) = @_; return $self->{distribution}; } sub license { return 'perl'; } sub meta_merge { my ( undef, @extra ) = @_; return { 'meta-spec' => { version => 2, }, dynamic_config => 1, resources => { bugtracker => { web => 'https://rt.cpan.org/Public/Dist/Display.html?Name=PPIx-Regexp', # web => 'https://github.com/trwyant/perl-PPIx-Regexp/issues', mailto => 'wyant@cpan.org', }, license => 'http://dev.perl.org/licenses/', repository => { type => 'git', url => 'git://github.com/trwyant/perl-PPIx-Regexp.git', web => 'https://github.com/trwyant/perl-PPIx-Regexp', }, }, @extra, }; } sub module_name { return 'PPIx::Regexp'; } sub no_index { return +{ directory => [ qw{ eg inc t tools xt } ], }; } sub provides { my $provides; local $@ = undef; eval { require CPAN::Meta; require ExtUtils::Manifest; require Module::Metadata; my $manifest; { local $SIG{__WARN__} = sub {}; $manifest = ExtUtils::Manifest::maniread(); } keys %{ $manifest || {} } or return; # Skeleton so we can use should_index_file() and # should_index_package(). my $meta = CPAN::Meta->new( { name => 'Euler', version => 2.71828, no_index => no_index(), }, ); # The Module::Metadata docs say not to use # package_versions_from_directory() directly, but the 'files =>' # version of provides() is broken, and has been known to be so # since 2014, so it's not getting fixed any time soon. So: foreach my $fn ( sort keys %{ $manifest } ) { $fn =~ m/ [.] pm \z /smx or next; my $pvd = Module::Metadata->package_versions_from_directory( undef, [ $fn ] ); foreach my $pkg ( keys %{ $pvd } ) { $meta->should_index_package( $pkg ) and $meta->should_index_file( $pvd->{$pkg}{file} ) and $provides->{$pkg} = $pvd->{$pkg}; } } 1; } or return; return ( provides => $provides ); } sub requires { my ( undef, @extra ) = @_; # Invocant unused ## if ( ! $self->distribution() ) { ## } return { 'Carp' => 0, 'Encode' => 0, 'Exporter' => 0, 'List::Util' => 0, 'PPI::Document' => 1.117, # for new( readonly => 1 ) 'PPI::Dumper' => 1.117, 'Scalar::Util' => 0, 'Task::Weaken' => 0, 'Text::Tabs' => 0, 'base' => 0, 'constant' => 0, 'strict' => 0, 'warnings' => 0, @extra, }; } sub requires_perl { return 5.006; } sub script_files { return [ ]; } sub version_from { return 'lib/PPIx/Regexp.pm'; } 1; __END__ =head1 NAME My::Module::Meta - Information needed to build PPIx::Regexp =head1 SYNOPSIS use lib qw{ inc }; use My::Module::Meta; my $meta = My::Module::Meta->new(); use YAML; print "Required modules:\n", Dump( $meta->requires() ); =head1 DETAILS This module centralizes information needed to build C. It is private to the C package, and may be changed or retracted without notice. =head1 METHODS This class supports the following public methods: =head2 new my $meta = PPIx::Meta->new(); This method instantiates the class. =head2 abstract This method returns the distribution's abstract. =head2 add_to_cleanup This method returns a reference to an array of files to be added to the cleanup. =head2 author This method returns the name of the distribution author =head2 build_requires use YAML; print Dump( $meta->build_requires() ); This method computes and returns a reference to a hash describing the modules required to build the C package, suitable for use in a F C key, or a F C<< {META_MERGE}->{build_requires} >> or C key. =head2 configure_requires use YAML; print Dump( $meta->configure_requires() ); This method returns a reference to a hash describing the modules required to configure the package, suitable for use in a F C key, or a F C<< {META_MERGE}->{configure_requires} >> or C key. =head2 dist_name This method returns the distribution name. =head2 distribution if ( $meta->distribution() ) { print "Making distribution\n"; } else { print "Not making distribution\n"; } This method returns the value of the environment variable C at the time the object was instantiated. =head2 license This method returns the distribution's license. =head2 meta_merge use YAML; print Dump( $meta->meta_merge() ); This method returns a reference to a hash describing the meta-data which has to be provided by making use of the builder's C functionality. This includes the C and C data. Any arguments will be appended to the generated array. =head2 module_name This method returns the name of the module the distribution is based on. =head2 no_index This method returns the names of things which are not to be indexed by CPAN. =head2 provides use YAML; print Dump( [ $meta->provides() ] ); This method attempts to load L. If this succeeds, it returns a C entry suitable for inclusion in L data (i.e. C<'provides'> followed by a hash reference). If it can not load the required module, it returns nothing. =head2 requires use YAML; print Dump( $meta->requires() ); This method computes and returns a reference to a hash describing the modules required to run the C package, suitable for use in a F C key, or a F C key. Any additional arguments will be appended to the generated hash. In addition, unless L is true, configuration-specific modules may be added. =head2 requires_perl print 'This package requires Perl ', $meta->requires_perl(), "\n"; This method returns the version of Perl required by the package. =head2 script_files This method returns a reference to an array containing the names of script files provided by this distribution. This array may be empty. =head2 version_from This method returns the name of the distribution file from which the distribution's version is to be derived. =head1 ATTRIBUTES This class has no public attributes. =head1 ENVIRONMENT =head2 MAKING_MODULE_DISTRIBUTION This environment variable should be set to a true value if you are making a distribution. This ensures that no configuration-specific information makes it into F. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2010-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/inc/My/Module/Mock_Tokenizer.pm000444000765000024 634014151156043 20662 0ustar00tomstaff000000000000package My::Module::Mock_Tokenizer; use 5.006; use strict; use warnings; use Carp; our $VERSION = '0.082'; use constant ARRAY_REF => ref []; sub new { my ( $class, %arg ) = @_; return bless \%arg, ref $class || $class; } sub capture { my ( $self ) = @_; ARRAY_REF eq ref $self->{capture} or return; return @{ $self->{capture} }; } sub cookie { my ( $self, $cookie ) = @_; return $self->{cookie}{$cookie}; } sub modifier_modify {} sub __recognize_postderef { my ( $self ) = @_; return $self->{postderef}; } 1; __END__ =head1 NAME My::Module::Mock_Tokenizer - Mock tokenizer for t/*.t =head1 SYNOPSIS use lib qw{ inc }; use My::Module::Mock_Tokenizer; my $tokenizer = My::Module::Mock_Tokenizer->new(); =head1 DESCRIPTION This Perl class is private to the C package, and may be modified or retracted without notice. Documentation is for the benefit of the author. It represents a mock tokenizer to be used in testing. It implements those methods that the author finds useful. =head1 METHODS This class supports the following public methods: =head2 new my $tokenizer = My::Module::Mock_Tokenizer->new(); This static method instantiates the tokenizer. In addition to the invocant it takes arbitrary name/value pairs of arguments. These arguments are made into a hash, and a blessed reference to this hash is returned. The arguments are not validated, but may be used in methods as documented below. =head2 capture say "Capture: '$_'" for $tokenizer->capture(); If C<< $tokenizer->{capture} >> is an array reference, the contents of the array are returned. Otherwise nothing is returned. =head2 cookie my $cookie = $tokenizer->cookie( $name ); This method returns C<< $tokenizer->{cookie}{$name} >>. If you want to specify a value for this, recall that cookies are code references. =head2 modifier_modify $tokenizer->modifier_modify( i => 1 ); This method does nothing and returns nothing. =head2 __recognize_postderef $tokenizer->__recognize_postderef() and say 'We recognize postfix dereferences'; This method returns the value of C<< $tokenizer->{postderef} >>. =head1 SEE ALSO L =head1 SUPPORT This module is private to the C package. It is unsupported in the sense that the author reserves the right to modify or retract it without prior notice. Bug reports against this module will be accepted provided they document a problem with this module that results in spurious test results. Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Tom Wyant (wyant at cpan dot org) =head1 COPYRIGHT AND LICENSE Copyright (C) 2015-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/inc/My/Module/Test.pm000444000765000024 5225114151156043 16700 0ustar00tomstaff000000000000package My::Module::Test; use strict; use warnings; use Exporter; our @ISA = ( qw{ Exporter } ); use PPIx::Regexp; use PPIx::Regexp::Dumper; use PPIx::Regexp::Element; use PPIx::Regexp::Tokenizer; use PPIx::Regexp::Util qw{ __choose_tokenizer_class __instance }; use Scalar::Util qw{ looks_like_number refaddr }; use Test::More 0.88; our $VERSION = '0.082'; use constant ARRAY_REF => ref []; our @EXPORT_OK = qw{ builder cache_count choose klass cmp_ok content count diag different done_testing dump_result error fail false finis equals is navigate note ok parse pass plan ppi result replace_characters skip tokenize true value }; our @EXPORT = @EXPORT_OK; ## no critic (ProhibitAutomaticExportation) push @EXPORT_OK, qw{ __quote }; my ( $initial_class, # For static methods; set by parse() or tokenize() $kind, # of thing; set by parse() or tokenize() $nav, # Navigation used to get to current object, as a # string. $obj, # Current object: # PPIx::Regexp::Tokenizer if set by tokenize(), # PPIx::Regexp if set by parse(), or # PPIx::Regexp::Element if set by navigate(). $parse, # Result of parse: # array ref if set by tokenize(), or # PPIx::Regexp object if set by parse() %replace_characters, # Troublesome characters replaced in output # before testing $result, # Operation result. ); sub builder { return Test::More->builder(); } sub cache_count { my ( $expect ) = @_; defined $expect or $expect = 0; $obj = undef; $parse = undef; _pause(); $result = PPIx::Regexp->__cache_size(); # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; return is( $result, $expect, "Should be $expect leftover cache contents" ); } sub choose { my @args = @_; $obj = $parse; return navigate( @args ); } sub klass { my ( $class ) = @_; $result = ref $obj || $obj; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( defined $class ) { return isa_ok( $obj, $class, "$kind $nav" ); } else { return is( ref $obj || undef, $class, "Class of $kind $nav" ); } } sub content { ## no critic (RequireArgUnpacking) # For some reason cperl seems to have no problem with this unshift @_, 'content'; goto &_method_result; } sub count { my ( @args ) = @_; my $expect = pop @args; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( ARRAY_REF eq ref $parse ) { $result = @{ $parse }; return is( $result, $expect, "Expect $expect tokens" ); } elsif ( ARRAY_REF eq ref $obj ) { $result = @{ $obj }; return is( $result, $expect, "Expect $expect tokens" ); } elsif ( $obj->can( 'children' ) ) { $result = $obj->children(); return is( $result, $expect, "Expect $expect children" ); } else { $result = $obj->can( 'children' ); return ok( $result, ref( $obj ) . "->can( 'children')" ); } } sub different { my @args = @_; @args < 3 and unshift @args, $obj; my ( $left, $right, $name ) = @args; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( ! defined $left && ! defined $right ) { return ok( undef, $name ); } elsif ( ! defined $left || ! defined $right ) { return ok( 1, $name ); } elsif ( ref $left && ref $right ) { return ok( refaddr( $left ) != refaddr( $right ), $name ); } elsif ( ref $left || ref $right ) { return ok( 1, $name ); } elsif ( looks_like_number( $left ) && looks_like_number( $right ) ) { return ok( $left != $right, $name ); } else { return ok( $left ne $right, $name ); } } sub dump_result { my ( $opt, @args ) = _parse_constructor_args( { test => 1 }, @_ ); if ( $opt->{test} ) { my ( $expect, $name ) = splice @args, -2; my $got = PPIx::Regexp::Dumper->new( $obj, @args )->string(); # cperl does not seem to like goto &xxx; it throws a deep # recursion error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; return is( $got, $expect, $name ); } elsif ( __instance( $result, 'PPIx::Regexp::Tokenizer' ) || __instance( $result, 'PPIx::Regexp::Element' ) ) { diag( PPIx::Regexp::Dumper->new( $obj, @args )->string() ); } elsif ( eval { require YAML; 1; } ) { diag( "Result dump:\n", YAML::Dump( $result ) ); } elsif ( eval { require Data::Dumper; 1 } ) { diag( "Result dump:\n", Data::Dumper::Dumper( $result ) ); } else { diag( "Result dump unavailable.\n" ); } return; } sub equals { my @args = @_; @args < 3 and unshift @args, $obj; my ( $left, $right, $name ) = @args; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( ! defined $left && ! defined $right ) { return ok( 1, $name ); } elsif ( ! defined $left || ! defined $right ) { return ok( undef, $name ); } elsif ( ref $left && ref $right ) { return ok( refaddr( $left ) == refaddr( $right ), $name ); } elsif ( ref $left || ref $right ) { return ok( undef, $name ); } elsif ( looks_like_number( $left ) && looks_like_number( $right ) ) { return ok( $left == $right, $name ); } else { return ok( $left eq $right, $name ); } } sub error { ## no critic (RequireArgUnpacking) unshift @_, 'error'; goto &_method_result; } sub false { my ( $method, $args ) = @_; ARRAY_REF eq ref $args or $args = [ $args ]; my $class = ref $obj; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( $obj->can( $method ) ) { $result = $obj->$method( @{ $args } ); my $fmtd = _format_args( $args ); return ok( ! $result, "$class->$method$fmtd is false" ); } else { $result = undef; return ok( undef, "$class->$method() exists" ); } } sub finis { $obj = $parse = $result = undef; _pause(); $result = PPIx::Regexp::Element->__parent_keys(); # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; return is( $result, 0, 'Should be no leftover objects' ); } { my %array = map { $_ => 1 } qw{ children delimiters finish schildren start tokens type }; sub navigate { my @args = @_; my $scalar = 1; @args > 1 and ARRAY_REF eq ref $args[-1] and @{ $args[-1] } == 0 and $array{$args[-2]} and $scalar = 0; my @nav = (); while ( @args ) { if ( __instance( $args[0], 'PPIx::Regexp::Element' ) ) { $obj = shift @args; } elsif ( ARRAY_REF eq ref $obj ) { my $inx = shift @args; push @nav, $inx; $obj = $obj->[$inx]; } else { my $method = shift @args; my $args = shift @args; ARRAY_REF eq ref $args or $args = [ $args ]; push @nav, $method, $args; $obj->can( $method ) or return; if ( @args || $scalar ) { $obj = $obj->$method( @{ $args } ) or return; } else { $obj = [ $obj->$method( @{ $args } ) ]; } } } $nav = __quote( @nav ); $nav =~ s/ ' ( \w+ ) ' , /$1 =>/smxg; $nav =~ s/ \[ \s+ \] /[]/smxg; $result = $obj; return $obj; } } sub parse { ## no critic (RequireArgUnpacking) my ( $opt, $regexp, @args ) = _parse_constructor_args( { test => 1 }, @_ ); $initial_class = 'PPIx::Regexp'; $kind = 'element'; $result = $obj = $parse = PPIx::Regexp->new( $regexp, @args ); $nav = ''; $opt->{test} or return; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; return isa_ok( $parse, 'PPIx::Regexp', _replace_characters( $regexp ) ); } sub ppi { ## no critic (RequireArgUnpacking) my @args = @_; my $expect = pop @args; $result = undef; defined $obj and $result = $obj->ppi()->content(); my $safe; if ( defined $result ) { ($safe = $result) =~ s/([\\'])/\\$1/smxg; } else { $safe = 'undef'; } # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; return is( $result, $expect, "$kind $nav ppi() content '$safe'" ); } sub replace_characters { %replace_characters = @_; return; } sub result { return $result; } sub tokenize { ## no critic (RequireArgUnpacking) my ( $opt, $regexp, @args ) = _parse_constructor_args( { test => 1, tokens => 1 }, @_ ); my %args = @args; $initial_class = __choose_tokenizer_class( $regexp, \%args ); $kind = 'token'; $obj = $initial_class->new( $regexp, @args ); if ( $obj && $opt->{tokens} ) { $parse = [ $obj->tokens() ]; } else { $parse = []; } $result = $parse; $nav = ''; $opt->{test} or return; $Test::Builder::Level = $Test::Builder::Level + 1; return isa_ok( $obj, 'PPIx::Regexp::Tokenizer', _replace_characters( $regexp ) ); } sub true { ## no critic (RequireArgUnpacking) my ( $method, $args ) = @_; ARRAY_REF eq ref $args or $args = [ $args ]; my $class = ref $obj; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( $obj->can( $method ) ) { $result = $obj->$method( @{ $args } ); my $fmtd = _format_args( $args ); return ok( $result, "$class->$method$fmtd is true" ); } else { $result = undef; return ok( undef, "$class->$method() exists" ); } } sub value { ## no critic (RequireArgUnpacking) my ( $method, $args, $expect ) = @_; ARRAY_REF eq ref $args or $args = [ $args ]; my $invocant = $obj || $initial_class; my $class = ref $obj || $obj || $initial_class; # cperl does not seem to like goto &xxx; it throws a deep recursion # error if you do it enough times. $Test::Builder::Level = $Test::Builder::Level + 1; if ( ! $invocant->can( $method ) ) { return ok( undef, "$class->$method() exists" ); } $result = ARRAY_REF eq ref $expect ? [ $invocant->$method( @{ $args } ) ] : $invocant->$method( @{ $args } ); my $fmtd = _format_args( $args ); my $answer = _format_args( [ $expect ], bare => 1 ); if ( ref $result ) { return is_deeply( $result, $expect, "${class}->$method$fmtd is $answer" ); } else { return is( $result, $expect, "${class}->$method$fmtd is $answer" ); } } sub _format_args { my ( $args, %opt ) = @_; my @rslt; foreach my $arg ( @{ $args } ) { if ( ! defined $arg ) { push @rslt, 'undef'; } elsif ( looks_like_number( $arg ) ) { push @rslt, $arg; } else { push @rslt, $arg; $rslt[-1] =~ s/ ' /\\'/smxg; $rslt[-1] = "'$rslt[-1]'"; } } my $string = join ', ', @rslt; $opt{bare} and return $string; @rslt or return '()'; return "( $string )"; } sub _method_result { ## no critic (RequireArgUnpacking) my ( $method, @args ) = @_; my $expect = pop @args; $result = undef; defined $obj and $result = $obj->$method(); my $safe; if ( defined $result ) { ($safe = $result) =~ s/([\\'])/\\$1/smxg; $safe = "'$safe'"; } else { $safe = 'undef'; } @_ = _replace_characters( $result, $expect, "$kind $nav $method $safe" ); goto &is; } sub _parse_constructor_args { my ( $opt, @args ) = @_; my @rslt = ( $opt ); foreach my $arg ( @args ) { if ( $arg =~ m/ \A - -? (no)? (\w+) \z /smx && exists $opt->{$2} ) { $opt->{$2} = !$1; } else { push @rslt, $arg; } } return @rslt; } sub _pause { if ( eval { require Time::HiRes; 1 } ) { # Cargo cult programming. Time::HiRes::sleep( 0.1 ); # Something like this is } else { # in PPI's sleep 1; # t/08_regression.t, and } # who am I to argue? return; } # quote a string. sub __quote { my @args = @_; my @rslt; foreach my $item ( @args ) { if ( __instance( $item, 'PPIx::Regexp::Element' ) ) { $item = $item->content(); } if ( ! defined $item ) { push @rslt, 'undef'; } elsif ( ARRAY_REF eq ref $item ) { push @rslt, join( ' ', '[', __quote( @{ $item } ), ']' ); } elsif ( looks_like_number( $item ) ) { push @rslt, $item; } else { $item =~ s/ ( [\\'] ) /\\$1/smxg; push @rslt, "'$item'"; } } return join( ', ', @rslt ); } sub _replace_characters { my @arg = @_; if ( keys %replace_characters ) { foreach ( @arg ) { $_ = join '', # The following assumes I will never want to replace 0. map { $replace_characters{$_} || $_ } split qr<>; } } wantarray or return join '', @arg; return @arg; } 1; __END__ =head1 NAME My::Module::Test - support for testing PPIx::Regexp =head1 SYNOPSIS use lib qw{ inc }; use My::Module::Test; parse ( '/foo/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); # and so on =head1 DETAILS This module is B to the C module. Its contents can be changed without warning. This was always the intent, and this paragraph should have been included in the POD much earlier than it actually was. This module exports various subroutines in support of testing C. Most of these are tests, with C doing the dirty work. A few simply set up data for tests. The whole test rig works by parsing (or tokenizing) a regular expression, followed by a series of unit tests on the results of the parse. Each set of unit tests is performed by selecting an object to test using the C or C subroutine, followed by the tests to be performed on that object. A few tests do not test parse objects, but rather the state of the system as a whole. The following subroutines are exported: =head2 builder This subroutine returns the underlying L object. =head2 cache_count cache_count( 1 ); This test compares the number of objects in the C cache to its argument, succeeding if they are equal. If no argument is passed, the default is 0. =head2 choose choose( 2 ); # For tokenizer choose( child => 1, child => 2, type => 0 ); # For full parse This subroutine does not itself represent a test. It chooses an object from the parse tree for further testing. If testing a tokenizer, the argument is the token number (from 0) to select. If testing a full parse, the arguments are the navigation methods used to reach the object to be tested, starting from the C object. The arguments to the methods are passed in an array reference, but if there is a single argument it can be passed as a scalar, as in the example. =head2 klass klass( 'PPIx::Regexp::Token::Structure' ); This test checks to see if the current object is of the given class, and succeeds if it is. If the current object is C, the test fails. This test was C, but that tends to conflict with object systems. =head2 content content( '\N{LATIN SMALL LETTER A}' ); This test checks to see if the C method of the current object is equal to the given string. If the current object is C, the test fails. =head2 cmp_ok This subroutine is exported from L. =head2 count count( 42 ); This test checks the number of objects returned by an operation that returns more than one object. It succeeds if the number of objects returned is equal to the given number. This test is valid only after C, or a C or C whose argument list ends in one of children => [] finish => [] start => [] type => [] =head2 different different( $o1, $o2, 'Test name' ); This test compares two things, succeeding if they are different. References are compared by reference address and scalars by value (numeric or string comparison as appropriate). If the first argument is omitted it defaults to the current object. =head2 dump_result dump_result( tokens => 1, <<'EOD', 'Test tokenization dump' ); ... expected dump here ... EOD This test performs the specified dump on the current object and succeeds if the result matches the expectation. The name of the test is the last argument, and the expected result is the next-to-last argument. All other arguments are passed to L. Well, almost all other arguments are passed to the dumper. You can specify C<--notest> to skip the test. In this case the result of the last operation is dumped. L is used if appropriate; otherwise you get a L dump if that is available, or a L dump if not. If no dumper class can be found, a diagnostic is produced. You can also specify C<--test>, but this is the default. This option is removed from the argument list before the test name (etc) is determined. =head2 equals equals( $o1, $o2, 'Test name' ); This test compares two things, succeeding if they are equal. References are compared by reference address and scalars by value (numeric or string comparison as appropriate). If the first argument is omitted it defaults to the current object. =head2 false false( significant => [] ); This test succeeds if the given method, with the given arguments, called on the current object, returns a false value. =head2 finis finis(); This test should be last in a series, and no references to parse objects should be held when it is run. It checks the number of objects in the internal C<%parent> hash, and succeeds if it is zero. =head2 navigate navigate( snext_sibling => [] ); Like C, this is not a test, but selects an object for testing. Unlike C, selection starts from the current object, not the top of the parse tree. =head2 parse parse( 's/foo/bar/g' ); This test parses the given regular expression into a C object, and succeeds if a C object was in fact generated. If you specify argument C<--notest>, the parse is done but no test is performed. You would do this if you expected the parse to fail (e.g. you are testing error handling). You can also explicitly specify C<--test>, but this is the default. All other arguments are passed to the L constructor. =head2 plan This subroutine is exported from L. =head2 content ppi( '$foo' ); This test calls the current object's C method, and checks to see if the content of the returned L is equal to the given string. If the current object is C or does not have a C method, the test fails. =head2 result my $val = result(); This subroutine returns the result of the most recent operation that actually produces one. It should be called immediately after the operation, mostly because I have not documented all the subroutines that produce a result. =head2 tokenize tokenize( 'm/foo/smx' ); This test tokenizes the given regular expression into a C object, and succeeds if a C object was in fact generated. If you specify argument C<--notest>, the parse is done but no test is performed. You would do this if you expected the parse to fail (e.g. you are testing error handling). You can also explicitly specify C<--test>, but this is the default. If you specify argument C<--notokens>, the tokenizer is built, but the tokens are not extracted. You would do this when you want a subsequent operation to call C. You can also explicitly specify C<--tokens>, but this is the default. All other arguments are passed to the L constructor. =head2 true true( significant => [] ); This test succeeds if the given method, with the given arguments, called on the current object, returns a true value. =head2 value value( max_capture_number => [], 3 ); This test succeeds if the given method, with the given arguments, called on the current object, returns the given value. If the current object is undefined, the given method is called on the intended initial class, otherwise there would be no way to test the errstr() method. The result of the method call is accessable via the L subroutine. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib000755000765000024 014151156043 13424 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx000755000765000024 014151156043 14244 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp.pm000444000765000024 7567214151156043 16232 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp - Represent a regular expression of some sort =head1 SYNOPSIS use PPIx::Regexp; use PPIx::Regexp::Dumper; my $re = PPIx::Regexp->new( 'qr{foo}smx' ); PPIx::Regexp::Dumper->new( $re ) ->print(); =head1 DEPRECATION NOTICE The L argument to L is being put through a deprecation cycle and retracted. After the retraction, postfix dereferences will always be recognized. This is the default behaviour now. Starting with version 0.074_01, the first use of this argument warned. warn. With version 0.079_01, all uses will warn. With version 0.080_01, all uses will become fatal. With the first release on or after April 15 2022 all mention of this argument will be removed. =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION The purpose of the F package is to parse regular expressions in a manner similar to the way the L package parses Perl. This class forms the root of the parse tree, playing a role similar to L. This package shares with L the property of being round-trip safe. That is, my $expr = 's/ ( \d+ ) ( \D+ ) /$2$1/smxg'; my $re = PPIx::Regexp->new( $expr ); print $re->content() eq $expr ? "yes\n" : "no\n" should print 'yes' for any valid regular expression. Navigation is similar to that provided by L. That is to say, things like C, C, C and so on all work pretty much the same way as in L. The class hierarchy is also similar to L. Except for some utility classes (the dumper, the lexer, and the tokenizer) all classes are descended from L, which provides basic navigation. Tokens are descended from L, which provides content. All containers are descended from L, which provides for children, and all structure elements are descended from L, which provides beginning and ending delimiters, and a type. There are two features of L that this package does not provide - mutability and operator overloading. There are no plans for serious mutability, though something like L's C functionality might be considered. Similarly there are no plans for operator overloading, which appears to the author to represent a performance hit for little tangible gain. =head1 NOTICE The author will attempt to preserve the documented interface, but if the interface needs to change to correct some egregiously bad design or implementation decision, then it will change. Any incompatible changes will go through a deprecation cycle. The goal of this package is to parse well-formed regular expressions correctly. A secondary goal is not to blow up on ill-formed regular expressions. The correct identification and characterization of ill-formed regular expressions is B a goal of this package, nor is the consistent parsing of ill-formed regular expressions from release to release. This policy attempts to track features in development releases as well as public releases. However, features added in a development release and then removed before the next production release B be tracked, and any functionality relating to such features B. The issue here is the potential re-use (with different semantics) of syntax that did not make it into the production release. From time to time the Perl regular expression engine changes in ways that change the parse of a given regular expression. When these changes occur, C will be changed to produce the more modern parse. Known examples of this include: =over =item C<$(> no longer interpolates as of Perl 5.005, per C. Newer Perls seem to parse this as C (i.e. an end-of-string or newline assertion) followed by an open parenthesis, and that is what C does. =item C<$)> and C<$|> also seem to parse as the C<$> assertion followed by the relevant meta-character, though I have no documentation reference for this. =item C<@+> and C<@-> no longer interpolate as of Perl 5.9.4 per C. Subsequent Perls treat C<@+> as a quantified literal and C<@-> as two literals, and that is what C does. Note that subscripted references to these arrays B interpolate, and are so parsed by C. =item Only space and horizontal tab are whitespace as of Perl 5.23.4 when inside a bracketed character class inside an extended bracketed character class, per C. Formerly any white space character parsed as whitespace. This change in C will be reverted if the change in Perl does not make it into Perl 5.24.0. =item Unescaped literal left curly brackets These are being removed in positions where quantifiers are legal, so that they can be used for new functionality. Some of them are gone in 5.25.1, others will be removed in a future version of Perl. In situations where they have been removed, L will return the version in which they were removed. When the new functionality appears, the parse produced by this software will reflect the new functionality. B that the situation with a literal left curly after a literal character is complicated. It was made an error in Perl 5.25.1, and remained so through all 5.26 releases, but became a warning again in 5.27.1 due to its use in GNU Autoconf. Whether it will ever become illegal again is not clear to me based on the contents of F. At the moment L returns C, but obviously that is not the whole story, and methods L and L were introduced to deal with this complication. =item C<\o{...}> is parsed as the octal equivalent of C<\x{...}>. This is its meaning as of perl 5.13.2. Before 5.13.2 it was simply literal C<'o'> and so on. =item C (with first count omitted) is allowed as a quantifier as of Perl 5.33.6. The previous parse made this all literals. =item C (with spaces inside but adjacent to curly brackets, or around the comma if any) is allowed as a quantifier as of Perl 5.33.6. The previous parse made this all literals. =back There are very probably other examples of this. When they come to light they will be documented as producing the modern parse, and the code modified to produce this parse if necessary. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp; use strict; use warnings; use base qw{ PPIx::Regexp::Node }; use Carp; use PPIx::Regexp::Constant qw{ ARRAY_REF LOCATION_LINE LOCATION_CHARACTER LOCATION_COLUMN LOCATION_LOGICAL_LINE LOCATION_LOGICAL_FILE @CARP_NOT }; use PPIx::Regexp::Lexer (); use PPIx::Regexp::Token::Modifier (); # For its modifier manipulations. use PPIx::Regexp::Tokenizer; use PPIx::Regexp::Util qw{ __choose_tokenizer_class __instance }; use Scalar::Util qw{ refaddr }; use Text::Tabs (); our $VERSION = '0.082'; =head2 new my $re = PPIx::Regexp->new('/foo/'); This method instantiates a C object from a string, a L, a L, or a L. Honestly, any L will work, but only the three Regexp classes mentioned previously are likely to do anything useful. Whatever form the argument takes, it is assumed to consist entirely of a valid match, substitution, or C<< qr<> >> string. Optionally you can pass one or more name/value pairs after the regular expression. The possible options are: =over =item default_modifiers array_reference This option specifies a reference to an array of default modifiers to apply to the regular expression being parsed. Each modifier is specified as a string. Any actual modifiers found supersede the defaults. When applying the defaults, C<'?'> and C<'/'> are completely ignored, and C<'^'> is ignored unless it occurs at the beginning of the modifier. The first dash (C<'-'>) causes subsequent modifiers to be negated. So, for example, if you wish to produce a C object representing the regular expression in use re '/smx'; { no re '/x'; m/ foo /; } you would (after some help from L in finding the relevant statements), do something like my $re = PPIx::Regexp->new( 'm/ foo /', default_modifiers => [ '/smx', '-/x' ] ); =item encoding name This option specifies the encoding of the regular expression. This is passed to the tokenizer, which will C the regular expression string before it tokenizes it. For example: my $re = PPIx::Regexp->new( '/foo/', encoding => 'iso-8859-1', ); =item index_locations Boolean This Boolean option specifies whether the locations of the elements in the regular expression should be indexed. If unspecified or specified as C a default value is used. This default is true if the argument is a L or the C option was specified. Otherwise the default is false. =item location array_reference This option specifies the location of the new object in the document from which it was created. It is a reference to a five-element array compatible with that returned by the C method of L. If not specified, the location of the original string is used if it was specified as a L. If no location can be determined, the various C methods will return C. =item postderef Boolean B. See L above for the details. This option is passed on to the tokenizer, where it specifies whether postfix dereferences are recognized in interpolations and code. This experimental feature was introduced in Perl 5.19.5. As of version 0.074_01, the default is true. Through release 0.074, the default was the value of C<$PPIx::Regexp::Tokenizer::DEFAULT_POSTDEREF>, which was true. When originally introduced this was false, but was documented as becoming true when and if postfix dereferencing became mainstream. The intent to mainstream was announced with Perl 5.23.1, and became official (so to speak) with Perl 5.24.0, so the default became true with L 0.049_01. Note that if L starts unconditionally recognizing postfix dereferences, this argument will immediately become ignored, and will be put through a deprecation cycle and removed. =item strict Boolean This option is passed on to the tokenizer and lexer, where it specifies whether the parse should assume C is in effect. The C<'strict'> pragma was introduced in Perl 5.22, and its documentation says that it is experimental, and that there is no commitment to backward compatibility. The same applies to the parse produced when this option is asserted. Also, the usual caveat applies: if C ends up being retracted, this option and all related functionality will be also. Given the nature of C, you should expect that if you assert this option, regular expressions that previously parsed without error might no longer do so. If an element ends up being declared an error because this option is set, its C will be the Perl version at which C started rejecting these elements. The default is false. =item trace number If greater than zero, this option causes trace output from the parse. The author reserves the right to change or eliminate this without notice. =back Passing optional input other than the above is not an error, but neither is it supported. =cut { my $errstr; sub new { my ( $class, $content, %args ) = @_; ref $class and $class = ref $class; # We have to do this very early so the tokenizer can see it. defined $args{index_locations} or $args{index_locations} = ( !! $args{location} || __instance( $content, 'PPI::Element' ) ); $errstr = undef; # As of 0.068_01 this either fails or returns # PPIx::Regexp::Tokenizer my $tokenizer_class = __choose_tokenizer_class( $content, \%args ); my $tokenizer = $tokenizer_class->new( $content, %args ) or do { $errstr = PPIx::Regexp::Tokenizer->errstr(); return; }; my $lexer = PPIx::Regexp::Lexer->new( $tokenizer, %args ); my @nodes = $lexer->lex(); my $self = $class->SUPER::__new( @nodes ); $self->{index_locations} = $args{index_locations}; $self->{source} = $content; $self->{failures} = $lexer->failures(); $self->{effective_modifiers} = $tokenizer->__effective_modifiers(); if ( $args{location} ) { ARRAY_REF eq ref $args{location} or croak q; foreach my $inx ( 0 .. 3 ) { $args{location}[$inx] =~ m/ [^0-9] /smx and croak "Argument 'location' element $inx must be an unsigned integer"; } $self->{location} = $args{location}; } return $self; } sub errstr { return $errstr; } } =head2 new_from_cache This static method wraps L in a caching mechanism. Only one object will be generated for a given L, no matter how many times this method is called. Calls after the first for a given L simply return the same C object. When the C object is returned from cache, the values of the optional arguments are ignored. Calls to this method with the regular expression in a string rather than a L will not be cached. B This method is provided for code like L which might instantiate the same object multiple times. The cache will persist until L is called. =head2 flush_cache $re->flush_cache(); # Remove $re from cache PPIx::Regexp->flush_cache(); # Empty the cache This method flushes the cache used by L. If called as a static method with no arguments, the entire cache is emptied. Otherwise any objects specified are removed from the cache. =cut { my %cache; our $DISABLE_CACHE; # Leave this undocumented, at least for # now. sub __cache_size { return scalar keys %cache; } sub new_from_cache { my ( $class, $content, %args ) = @_; __instance( $content, 'PPI::Element' ) or return $class->new( $content, %args ); $DISABLE_CACHE and return $class->new( $content, %args ); my $addr = refaddr( $content ); exists $cache{$addr} and return $cache{$addr}; my $self = $class->new( $content, %args ) or return; $cache{$addr} = $self; return $self; } sub flush_cache { my @args = @_; ref $args[0] or shift @args; if ( @args ) { foreach my $obj ( @args ) { if ( __instance( $obj, __PACKAGE__ ) && __instance( ( my $parent = $obj->source() ), 'PPI::Element' ) ) { delete $cache{ refaddr( $parent ) }; } } } else { %cache = (); } return; } } sub can_be_quantified { return; } =head2 capture_names foreach my $name ( $re->capture_names() ) { print "Capture name '$name'\n"; } This convenience method returns the capture names found in the regular expression. This method is equivalent to $self->regular_expression()->capture_names(); except that if C<< $self->regular_expression() >> returns C (meaning that something went terribly wrong with the parse) this method will simply return. =cut sub capture_names { my ( $self ) = @_; my $re = $self->regular_expression() or return; return $re->capture_names(); } =head2 delimiters print join("\t", PPIx::Regexp->new('s/foo/bar/')->delimiters()); # prints '// //' When called in list context, this method returns either one or two strings, depending on whether the parsed expression has a replacement string. In the case of non-bracketed substitutions, the start delimiter of the replacement string is considered to be the same as its finish delimiter, as illustrated by the above example. When called in scalar context, you get the delimiters of the regular expression; that is, element 0 of the array that is returned in list context. Optionally, you can pass an index value and the corresponding delimiters will be returned; index 0 represents the regular expression's delimiters, and index 1 represents the replacement string's delimiters, which may be undef. For example, print PPIx::Regexp->new('s{foo}')->delimiters(1); # prints '<>' If the object was not initialized with a valid regexp of some sort, the results of this method are undefined. =cut sub delimiters { my ( $self, $inx ) = @_; my @rslt; foreach my $method ( qw{ regular_expression replacement } ) { defined ( my $obj = $self->$method() ) or next; push @rslt, $obj->delimiters(); } defined $inx and return $rslt[$inx]; wantarray and return @rslt; defined wantarray and return $rslt[0]; return; } =head2 errstr This static method returns the error string from the most recent attempt to instantiate a C. It will be C if the most recent attempt succeeded. =cut # defined above, just after sub new. sub explain { return; } =head2 extract_regexps my $doc = PPI::Document->new( $path ); $doc->index_locations(); my @res = PPIx::Regexp->extract_regexps( $doc ) This convenience (well, sort-of) static method takes as its argument a L object and returns C objects corresponding to all regular expressions found in it, in the order in which they occur in the document. You will need to keep a reference to the original L object if you wish to be able to recover the original L objects via the L L method. =cut sub extract_regexps { my ( $class, $doc ) = @_; my @found = map { @{ $doc->find( $_ ) || [] } } qw{ PPI::Token::QuoteLike::Regexp PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute }; return ( map { $class->new( $_ ) } map { $_->[0] } sort { $a->[1][0] <=> $b->[1][0] || $a->[1][1] <=> $b->[1][1] } map { [ $_, $_->location() ] } @found ); } =head2 failures print "There were ", $re->failures(), " parse failures\n"; This method returns the number of parse failures. This is a count of the number of unknown tokens plus the number of unterminated structures plus the number of unmatched right brackets of any sort. =cut sub failures { my ( $self ) = @_; return $self->{failures}; } =head2 max_capture_number print "Highest used capture number ", $re->max_capture_number(), "\n"; This convenience method returns the highest capture number used by the regular expression. If there are no captures, the return will be 0. This method is equivalent to $self->regular_expression()->max_capture_number(); except that if C<< $self->regular_expression() >> returns C (meaning that something went terribly wrong with the parse) this method will too. =cut sub max_capture_number { my ( $self ) = @_; my $re = $self->regular_expression() or return; return $re->max_capture_number(); } =head2 modifier my $re = PPIx::Regexp->new( 's/(foo)/${1}bar/smx' ); print $re->modifier()->content(), "\n"; # prints 'smx'. This method retrieves the modifier of the object. This comes from the end of the initializing string or object and will be a L. B that this object represents the actual modifiers present on the regexp, and does not take into account any that may have been applied by default (i.e. via the C argument to C). For something that takes account of default modifiers, see L, below. In the event of a parse failure, there may not be a modifier present, in which case nothing is returned. =cut sub modifier { my ( $self ) = @_; return $self->_component( 'PPIx::Regexp::Token::Modifier' ); } =head2 modifier_asserted my $re = PPIx::Regexp->new( '/ . /', default_modifiers => [ 'smx' ] ); print $re->modifier_asserted( 'x' ) ? "yes\n" : "no\n"; # prints 'yes'. This method returns true if the given modifier is asserted for the regexp, whether explicitly or by the modifiers passed in the C argument. Starting with version 0.036_01, if the argument is a single-character modifier followed by an asterisk (intended as a wild card character), the return is the number of times that modifier appears. In this case an exception will be thrown if you specify a multi-character modifier (e.g. C<'ee*'>), or if you specify one of the match semantics modifiers (e.g. C<'a*'>). =cut sub modifier_asserted { my ( $self, $modifier ) = @_; return PPIx::Regexp::Token::Modifier::__asserts( $self->{effective_modifiers}, $modifier, ); } # This is a kluge for both determining whether the object asserts # modifiers (hence the 'ductype') and determining whether the given # modifier is actually asserted. The signature is the invocant and the # modifier name, which must not be undef. The return is a Boolean. *__ducktype_modifier_asserted = \&modifier_asserted; # As of Perl 5.21.1 you can not leave off the type of a '?'-delimited # regexp. Because this is not associated with any single child we # compute it here. sub perl_version_removed { my ( $self ) = @_; my $v = $self->SUPER::perl_version_removed(); defined $v and $v <= 5.021001 and return $v; defined( my $delim = $self->delimiters() ) or return $v; '??' eq $delim and '' eq $self->type()->content() and return '5.021001'; return $v; } =head2 regular_expression my $re = PPIx::Regexp->new( 's/(foo)/${1}bar/smx' ); print $re->regular_expression()->content(), "\n"; # prints '/(foo)/'. This method returns that portion of the object which actually represents a regular expression. =cut sub regular_expression { my ( $self ) = @_; return $self->_component( 'PPIx::Regexp::Structure::Regexp' ); } =head2 replacement my $re = PPIx::Regexp->new( 's/(foo)/${1}bar/smx' ); print $re->replacement()->content(), "\n"; # prints '${1}bar/'. This method returns that portion of the object which represents the replacement string. This will be C unless the regular expression actually has a replacement string. Delimiters will be included, but there will be no beginning delimiter unless the regular expression was bracketed. =cut sub replacement { my ( $self ) = @_; return $self->_component( 'PPIx::Regexp::Structure::Replacement' ); } =head2 source my $source = $re->source(); This method returns the object or string that was used to instantiate the object. =cut sub source { my ( $self ) = @_; return $self->{source}; } =head2 type my $re = PPIx::Regexp->new( 's/(foo)/${1}bar/smx' ); print $re->type()->content(), "\n"; # prints 's'. This method retrieves the type of the object. This comes from the beginning of the initializing string or object, and will be a L whose C is one of 's', 'm', 'qr', or ''. =cut sub type { my ( $self ) = @_; return $self->_component( 'PPIx::Regexp::Token::Structure' ); } sub _component { my ( $self, $class ) = @_; foreach my $elem ( $self->children() ) { $elem->isa( $class ) and return $elem; } return; } 1; __END__ =head1 RESTRICTIONS By the nature of this module, it is never going to get everything right. Many of the known problem areas involve interpolations one way or another. =head2 Ambiguous Syntax Perl's regular expressions contain cases where the syntax is ambiguous. A particularly egregious example is an interpolation followed by square or curly brackets, for example C<$foo[...]>. There is nothing in the syntax to say whether the programmer wanted to interpolate an element of array C<@foo>, or whether he wanted to interpolate scalar C<$foo>, and then follow that interpolation by a character class. The F documentation notes that in this case what Perl does is to guess. That is, it employs various heuristics on the code to try to figure out what the programmer wanted. These heuristics are documented as being undocumented (!) and subject to change without notice. As an example of the problems even F faces in parsing Perl, see L. Given this situation, this module's chances of duplicating every Perl version's interpretation of every regular expression are pretty much nil. What it does now is to assume that square brackets containing B an integer or an interpolation represent a subscript; otherwise they represent a character class. Similarly, curly brackets containing B a bareword or an interpolation are a subscript; otherwise they represent a quantifier. =head2 Changes in Syntax Sometimes the introduction of new syntax changes the way a regular expression is parsed. For example, the C<\v> character class was introduced in Perl 5.9.5. But it did not represent a syntax error prior to that version of Perl, it was simply parsed as C. So $ perl -le 'print "v" =~ m/\v/ ? "yes" : "no"' prints "yes" under Perl 5.8.9, but "no" under 5.10.0. C generally assumes the more modern parse in cases like this. =head2 Equivocation Very occasionally, a construction will be removed and then added back -- and then, conceivably, removed again. In this case, the plan is for L to return the earliest version in which the construction appeared, and L to return the version after the last version in which it appeared (whether production or development), or C if it is in the highest-numbered Perl. The constructions involved in this are: =head3 Un-escaped literal left curly after literal That is, something like C<< qr >>. This was made an error in C<5.25.1>, and it was an error in C<5.26.0>. But it became a warning again in C<5.27.1>. The F says it was re-instated because the changes broke GNU Autoconf, and the warning message says it will be removed in Perl C<5.30>. Accordingly, L returns C<5.0>. At the moment L returns C<'5.025001'>. But if it is present with or without warning in C<5.28>, L will become C. If you need finer resolution than this, see L methods l and l =head2 Static Parsing It is well known that Perl can not be statically parsed. That is, you can not completely parse a piece of Perl code without executing that same code. Nevertheless, this class is trying to statically parse regular expressions. The main problem with this is that there is no way to know what is being interpolated into the regular expression by an interpolated variable. This is a problem because the interpolated value can change the interpretation of adjacent elements. This module deals with this by making assumptions about what is in an interpolated variable. These assumptions will not be enumerated here, but in general the principal is to assume the interpolated value does not change the interpretation of the regular expression. For example, my $foo = 'a-z]'; my $re = qr{[$foo}; is fine with the Perl interpreter, but will confuse the dickens out of this module. Similarly and more usefully, something like my $mods = 'i'; my $re = qr{(?$mods:foo)}; or maybe my $mods = 'i'; my $re = qr{(?$mods)$foo}; probably sets a modifier of some sort, and that is how this module interprets it. If the interpolation is B about modifiers, this module will get it wrong. Another such semi-benign example is my $foo = $] >= 5.010 ? '?' : ''; my $re = qr{($foo\w+)}; which will parse, but this module will never realize that it might be looking at a named capture. =head2 Non-Standard Syntax There are modules out there that alter the syntax of Perl. If the syntax of a regular expression is altered, this module has no way to understand that it has been altered, much less to adapt to the alteration. The following modules are known to cause problems: L, which renders Perl as XML. C, which causes Perl to interpret suffixed empty brackets as dereferencing the thing they suffix. This module by Ben Morrow (C) appears to have been retracted. L, which recognizes ANSI C trigraphs, allowing Perl to be written in the ISO 646 character set. L. Enough said. L, which back-ports some of the Perl 6 regular expression syntax to Perl 5. L, which extends regular expressions in various ways, some of which seem to conflict with Perl 5.010. =head1 SEE ALSO L, which uses L to parse the regexp, and L for navigation. Unlike C, L supports modification of the parse tree. L, which parses a bare regular expression (without enclosing C, C, or whatever) and uses a different navigation model. After a long hiatus, this module has been adopted, and is again supported. L, which provides the parse tree, and has a mechanism to subclass the various element classes for customization. The most-recent release is 2011, but the CPAN testers results are still all green. Companion module L says what the various pieces of a regex do, though constructs added in perl 5.10 and later are not supported. I have no idea how I missed this when I originally went looking for C parsers. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp000755000765000024 014151156043 15476 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Constant.pm000444000765000024 2770114151156043 20011 0ustar00tomstaff000000000000package PPIx::Regexp::Constant; use strict; use warnings; use base qw{ Exporter }; # CAVEAT: do not include any other PPIx-Regexp modules in this one, or # you will end up with a circular dependency. our $VERSION = '0.082'; our @EXPORT_OK = qw{ ARRAY_REF CODE_REF COOKIE_CLASS COOKIE_LOOKAROUND_ASSERTION COOKIE_QUANT COOKIE_QUOTE COOKIE_REGEX_SET FALSE HASH_REF LITERAL_LEFT_CURLY_ALLOWED LITERAL_LEFT_CURLY_REMOVED_PHASE_1 LITERAL_LEFT_CURLY_REMOVED_PHASE_2 LITERAL_LEFT_CURLY_REMOVED_PHASE_3 LOCATION_LINE LOCATION_CHARACTER LOCATION_COLUMN LOCATION_LOGICAL_LINE LOCATION_LOGICAL_FILE MINIMUM_PERL MODIFIER_GROUP_MATCH_SEMANTICS MSG_LOOK_BEHIND_TOO_LONG MSG_PROHIBITED_BY_STRICT NODE_UNKNOWN RE_CAPTURE_NAME REGEXP_REF SCALAR_REF STRUCTURE_UNKNOWN SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS TOKEN_LITERAL TOKEN_UNKNOWN TRUE VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED @CARP_NOT }; our @CARP_NOT = qw{ PPIx::Regexp PPIx::Regexp::Constant PPIx::Regexp::Dumper PPIx::Regexp::Element PPIx::Regexp::Lexer PPIx::Regexp::Node PPIx::Regexp::Node::Range PPIx::Regexp::Node::Unknown PPIx::Regexp::Structure PPIx::Regexp::Structure::Assertion PPIx::Regexp::Structure::Atomic_Script_Run PPIx::Regexp::Structure::BranchReset PPIx::Regexp::Structure::Capture PPIx::Regexp::Structure::CharClass PPIx::Regexp::Structure::Code PPIx::Regexp::Structure::Main PPIx::Regexp::Structure::Modifier PPIx::Regexp::Structure::NamedCapture PPIx::Regexp::Structure::Quantifier PPIx::Regexp::Structure::RegexSet PPIx::Regexp::Structure::Regexp PPIx::Regexp::Structure::Replacement PPIx::Regexp::Structure::Script_Run PPIx::Regexp::Structure::Subexpression PPIx::Regexp::Structure::Switch PPIx::Regexp::Structure::Unknown PPIx::Regexp::Support PPIx::Regexp::Token PPIx::Regexp::Token::Assertion PPIx::Regexp::Token::Backreference PPIx::Regexp::Token::Backtrack PPIx::Regexp::Token::CharClass PPIx::Regexp::Token::CharClass::POSIX PPIx::Regexp::Token::CharClass::POSIX::Unknown PPIx::Regexp::Token::CharClass::Simple PPIx::Regexp::Token::Code PPIx::Regexp::Token::Comment PPIx::Regexp::Token::Condition PPIx::Regexp::Token::Control PPIx::Regexp::Token::Delimiter PPIx::Regexp::Token::Greediness PPIx::Regexp::Token::GroupType PPIx::Regexp::Token::GroupType::Assertion PPIx::Regexp::Token::GroupType::Atomic_Script_Run PPIx::Regexp::Token::GroupType::BranchReset PPIx::Regexp::Token::GroupType::Code PPIx::Regexp::Token::GroupType::Modifier PPIx::Regexp::Token::GroupType::NamedCapture PPIx::Regexp::Token::GroupType::Script_Run PPIx::Regexp::Token::GroupType::Subexpression PPIx::Regexp::Token::GroupType::Switch PPIx::Regexp::Token::Interpolation PPIx::Regexp::Token::Literal PPIx::Regexp::Token::Modifier PPIx::Regexp::Token::NoOp PPIx::Regexp::Token::Operator PPIx::Regexp::Token::Quantifier PPIx::Regexp::Token::Recursion PPIx::Regexp::Token::Reference PPIx::Regexp::Token::Structure PPIx::Regexp::Token::Unknown PPIx::Regexp::Token::Unmatched PPIx::Regexp::Token::Whitespace PPIx::Regexp::Tokenizer PPIx::Regexp::Util }; use constant COOKIE_CLASS => ']'; use constant COOKIE_QUANT => '}'; use constant COOKIE_QUOTE => '\\E'; use constant COOKIE_REGEX_SET => '])'; use constant COOKIE_LOOKAROUND_ASSERTION => 'lookaround'; use constant FALSE => 0; use constant TRUE => 1; use constant ARRAY_REF => ref []; use constant CODE_REF => ref sub {}; use constant HASH_REF => ref {}; use constant REGEXP_REF => ref qr{}; use constant SCALAR_REF => ref \0; # In the cases where an unescaped literal left curly 'could not' be a # quantifier, they are allowed. At least, that was the original idea. # But read on. use constant LITERAL_LEFT_CURLY_ALLOWED => undef; # 'Most' unescaped literal left curlys were removed in 5.26. use constant LITERAL_LEFT_CURLY_REMOVED_PHASE_1 => '5.025001'; # Unescaped literal left curlys after literals and certain other # elements are scheduled to be removed in 5.30. use constant LITERAL_LEFT_CURLY_REMOVED_PHASE_2 => undef; # x{ 5.30 # In 5.27.8 it was decided that unescaped literal left curlys after an # open paren will be removed in 5.32. This does not include the case # where the entire regex is delimited by parens -- they are still legal # there. use constant LITERAL_LEFT_CURLY_REMOVED_PHASE_3 => undef; # ({ 5.32 # Location constants. Must align with PPI use constant LOCATION_LINE => 0; use constant LOCATION_CHARACTER => 1; use constant LOCATION_COLUMN => 2; use constant LOCATION_LOGICAL_LINE => 3; use constant LOCATION_LOGICAL_FILE => 4; use constant MINIMUM_PERL => '5.000'; use constant MODIFIER_GROUP_MATCH_SEMANTICS => 'match_semantics'; use constant MSG_LOOK_BEHIND_TOO_LONG => 'Lookbehind longer than 255 not implemented'; use constant MSG_PROHIBITED_BY_STRICT => q; use constant NODE_UNKNOWN => 'PPIx::Regexp::Node::Unknown'; # The perlre for Perl 5.010 says: # # Currently NAME is restricted to simple identifiers only. In # other words, it must match "/^[_A-Za-z][_A-Za-z0-9]*\z/" or # its Unicode extension (see utf8), though it isn't extended by # the locale (see perllocale). use constant RE_CAPTURE_NAME => ' [_[:alpha:]] \w* '; use constant STRUCTURE_UNKNOWN => 'PPIx::Regexp::Structure::Unknown'; use constant SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS => $] ge '5.008003'; use constant TOKEN_LITERAL => 'PPIx::Regexp::Token::Literal'; use constant TOKEN_UNKNOWN => 'PPIx::Regexp::Token::Unknown'; use constant VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED => '5.029009'; 1; __END__ =head1 NAME PPIx::Regexp::Constant - Constants for the PPIx::Regexp system =head1 SYNOPSIS use PPIx::Regexp::Constant qw{ TOKEN_UNKNOWN } print "An unknown token's class is TOKEN_UNKNOWN\n"; =head1 INHERITANCE C is an L. C has no descendants. =head1 DETAILS This module defines manifest constants for use by the various C modules. These constants are to be considered B to the C system, and the author reserves the right to change them without notice. This module exports the following manifest constants: =head2 @CARP_NOT This global variable contains the names of all modules in the package. =head2 ARRAY_REF This is the result of C. =head2 CODE_REF This is the result of C. =head2 COOKIE_CLASS The name of the cookie used to control the construction of character classes. This cookie is set in L when the left square bracket is encountered, and cleared in the same module when a right square bracket is encountered. =head2 COOKIE_LOOKAROUND_ASSERTION The name of the cookie used to control the parsing of zero-width assertions. This cookie is set in L, and it persists until the end of the assertion. =head2 COOKIE_QUANT The name of the cookie used to control the construction of curly bracketed quantifiers. This cookie is set in L when a left curly bracket is encountered. It requests itself to be cleared on encountering anything other than a literal comma, a literal digit, or an interpolation, or if more than one comma is encountered. If it survives until L processes the right curly bracket, it is cleared there. =head2 COOKIE_QUOTE The name of the cookie used to control the parsing of C<\Q ... \E> quoted literals. This cookie is set in L when a C<\Q> is encountered, and it persists until the next C<\E>. =head2 COOKIE_REGEX_SET The name of the cookie used to control regular expression sets. =head2 HASH_REF This is the result of C. =head2 LITERAL_LEFT_CURLY_ALLOWED The Perl version at which allowed unescaped literal left curly brackets were removed. This may make more sense if I mention that its value is C. =head2 LITERAL_LEFT_CURLY_REMOVED_PHASE_1 The Perl version at which the first phase of unescaped literal left curly bracket removal took place. The value of this constant is C<'5.025001'>. =head2 LITERAL_LEFT_CURLY_REMOVED_PHASE_2 The Perl version at which the second phase of unescaped literal left curly bracket removal took place. The value of this constant is C, but it will be assigned a value when the timing of the second phase is known. =head2 LITERAL_LEFT_CURLY_REMOVED_PHASE_3 The Perl version at which the third phase of unescaped literal left curly bracket removal took place. This is the removal of curly brackets after a left parenthesis. The value of this constant is C, but it will be assigned a value when the timing of the second phase is known. =head2 MINIMUM_PERL The minimum version of Perl understood by this parser, as a float. It is currently set to 5.000, since that is the minimum version of Perl accessible to the author. =head2 MODIFIER_GROUP_MATCH_SEMANTICS The name of the L group used to control match semantics. =head2 MSG_LOOK_BEHIND_TOO_LONG An appropriate error message for an unknown entity created from a quantifier which would make a look-behind assertion too long. This is cribbed verbatim from the Perl error message. =head2 MSG_PROHIBITED_BY_STRICT An appropriate error message for an unknown entity created because C<'strict'> was in effect. This is rank ad-hocery, and more than usually subject to being changed, without any notice whatsoever. Caveat user. =head2 NODE_UNKNOWN The name of the class that represents an unknown node. That is, L. =head2 RE_CAPTURE_NAME A string representation of a regular expression that matches the name of a named capture buffer. =head2 REGEXP_REF This is the result of C. =head2 SCALAR_REF This is the result of C. =head2 STRUCTURE_UNKNOWN The name of the class that represents an unknown structure. That is, L. =head2 SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS A Boolean which is true if the running version of Perl has UTF-8 support sufficient for our purposes. Currently that means C<5.8.3> or greater, with the specific requirements being C, C, and the ability to parse things like C. =head2 TOKEN_LITERAL The name of the class that represents a literal token. That is, L. =head2 TOKEN_UNKNOWN The name of the class that represents the unknown token. That is, L. =head2 VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED The version in which variable-length look-behinds were introduced. Currently this is C<'5.029009'>, and implies the limited lookbehind introduced at or about that version. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Dumper.pm000444000765000024 5450614151156043 17457 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Dumper - Dump the results of parsing regular expressions =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class generates a formatted dump of a L object (or any subclass thereof), a L object, or a string that can be made into one of these. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Dumper; use strict; use warnings; use base qw{ PPIx::Regexp::Support }; use Carp; use Scalar::Util qw{ blessed looks_like_number }; use PPIx::Regexp; use PPIx::Regexp::Constant qw{ ARRAY_REF @CARP_NOT }; use PPIx::Regexp::Tokenizer; use PPIx::Regexp::Util qw{ __choose_tokenizer_class __instance }; our $VERSION = '0.082'; use constant LOCATION_WIDTH => 19; =head2 new my $dumper = PPIx::Regexp::Dumper->new( '/foo/', ordinal => 1, ); This static method instantiates the dumper. It takes the string, L, L, or L to be dumped as the first argument. Optional further arguments may be passed as name/value pairs. The following options are recognized: =over =item default_modifiers array_reference This argument is a reference to a list of default modifiers to be applied to the statement being parsed. See L L for the details. =item encoding name This argument is the name of the encoding of the regular expression. If specified, it is passed through to L<< PPIx::Regexp->new()|PPIx::Regexp/new >>. It also causes an C to be done on any parse content dumped. =item explain Boolean If true, this option causes the C output of each object to be dumped. =item indent number This argument is the number of additional spaces to indent each level of the parse hierarchy. This is ignored if either the C or C argument is true. The default is 2. =item margin number This is the number of spaces to indent the top level of the parse hierarchy. This is ignored if the C argument is true. The default is zero. =item ordinal Boolean If true, this option causes the C values of L objects to be dumped. =item perl_version Boolean If true, this option causes the C and C values associated with each object dumped to be displayed. =item postderef Boolean B. See L in L for the details. If true, postfix dereferences are recognized in code and interpolations. See the tokenizer's L for details. =item ppi Boolean If true, any Perl code contained in the object will be dumped. =item short Boolean If true, leading C<'PPIx::Regexp::'> will be removed from the class names in the output. =item strict Boolean This option is passed on to the parser, where it specifies whether the parse should assume C is in effect. The C<'strict'> pragma was introduced in Perl 5.22, and its documentation says that it is experimental, and that there is no commitment to backward compatibility. The same applies to the parse produced when this option is asserted. The default is false. =item significant Boolean If true, this option causes only significant elements to be dumped. The default is false. =item test Boolean If true, this option causes the output to be formatted as a regression test rather than as a straight dump. The output produced by asserting this option is explicitly undocumented, in the sense that the author reserves the right to change the generated output without notice of any kind. The default is false. =item tokens Boolean If true, this option causes a dump of tokenizer output rather than of a full parse of the regular expression. This is forced true if the dump is of a L. The default is false. =item trace number If greater than zero, this option causes a trace of the parse. This option is unsupported in the sense that the author reserves the right to change it without notice. The default is zero. =item verbose number If greater than zero, this option causes additional information to be given about the elements found. This option is unsupported in the sense that the author reserves the right to change it without notice. The default is zero. =back If the thing to be dumped was a string, unrecognized arguments are passed to C<< PPIx::Regexp::Tokenizer->new() >>. Otherwise they are ignored. =cut { my %default = ( explain => 0, indent => 2, locations => 0, margin => 0, ordinal => 0, perl_version => 0, ppi => 0, short => 0, significant => 0, test => 0, tokens => 0, verbose => 0, ); sub new { my ( $class, $re, %args ) = @_; ref $class and $class = ref $class; my $self = { encoding => $args{encoding}, lister => undef, object => undef, source => $re, strict => $args{strict}, }; foreach my $key ( qw{ default_modifiers parse } ) { exists $args{$key} and $self->{$key} = $args{$key}; } foreach my $key ( keys %default ) { $self->{$key} = exists $args{$key} ? delete $args{$key} : $default{$key}; } $self->{ordinal} ||= $self->{verbose}; if ( __instance( $re, 'PPIx::Regexp::Tokenizer' ) ) { $self->{object} = $re; $self->{tokens} = 1; } elsif ( __instance( $re, 'PPIx::Regexp::Element' ) ) { $self->{object} = $re; } elsif ( ARRAY_REF eq ref $re ) { $self->{object} = $re; } elsif ( ref $re && ! __instance( $re, 'PPI::Element' ) ) { croak "Do not know how to dump ", ref $re; } elsif ( $self->{tokens} ) { my $tokenizer_class = __choose_tokenizer_class( $re, \%args ) or croak 'Unsupported data type'; __instance( $re, 'PPI::Element' ) or $args{location} = [ 1, 1, 1, 1, undef ]; $self->{object} = $tokenizer_class->new( $re, %args ) or Carp::croak( $tokenizer_class->errstr() ); } else { __instance( $re, 'PPI::Element' ) or $args{location} = [ 1, 1, 1, 1, undef ]; $self->{object} = PPIx::Regexp->new( $re, %args ) or Carp::croak( PPIx::Regexp->errstr() ); } bless $self, $class; return $self; } } =head2 list print map { "$_\n" } $dumper->list(); This method produces an array containing the dump output, one line per element. The output has no left margin applied, and no newlines. =cut sub list { my ( $self ) = @_; my $lister = $self->{test} ? '__PPIX_DUMPER__test' : '__PPIX_DUMPER__dump'; ARRAY_REF eq ref $self->{object} and return ( map { $_->$lister( $self ) } @{ $self->{object} } ); return $self->{object}->$lister( $self ); } =head2 print $dumper->print(); This method simply prints the result of L to standard out. =cut sub print : method { ## no critic (ProhibitBuiltinHomonyms) my ( $self ) = @_; # Non-characters and Non-Unicode code points are explicitly allowed # as delimiters, at least as of 5.29.0, which is where unassigned # and combining code points became illegal. Unfortunately the # warnings below were not introduced until 5.14, so have to go for # the next-higher warning category. # no warnings qw{ nonchar non_unicode }; ## no critic (ProhibitNoWarnings) no warnings qw{ utf8 }; ## no critic (ProhibitNoWarnings) print $self->string(); return; } =head2 string print $dumper->string(); This method adds left margin and newlines to the output of L, concatenates the result into a single string, and returns that string. =cut sub string { my ( $self ) = @_; my $margin = ' ' x $self->{margin}; return join( '', map { $margin . $_ . "\n" } $self->list() ); } # quote a string. sub _safe { my ( $self, @args ) = @_; my @rslt; foreach my $item ( @args ) { if ( blessed( $item ) ) { $item = $self->encode( $item->content() ); } if ( ! defined $item ) { push @rslt, 'undef'; } elsif ( ARRAY_REF eq ref $item ) { push @rslt, join( ' ', '[', $self->_safe( @{ $item } ), ']' ); } elsif ( looks_like_number( $item ) ) { push @rslt, $item; } else { $item =~ s/ ( [\\'] ) /\\$1/smxg; push @rslt, "'$item'"; } } my $rslt = join( ', ', @rslt ); return $rslt } sub _safe_version { my ( undef, $version ) = @_; # Invocant unused return defined $version ? "'$version'" : 'undef'; } sub __nav { my ( $self, @args ) = @_; my $rslt = $self->_safe( @args ); $rslt =~ s/ ' (\w+) ' , /$1 =>/smxg; $rslt =~ s/ \[ \s+ \] /[]/smxg; $rslt =~ s/ \[ \s* ( [0-9]+ ) \s* \] /$1/smxg; return $rslt; } sub _perl_version { my ( undef, $elem ) = @_; # Invocant unused return $elem->requirements_for_perl(); } sub _ppi { my ( $self, $elem ) = @_; $self->{ppi} and $elem->can( 'ppi' ) or return; require PPI::Dumper; # PPI::Dumper reports line_number(), but I want # logical_line_number(). There is no configuration for this, but the # interface is public, so I mung it to do what I want. my $locn = PPI::Element->can( 'location' ); local *PPI::Element::location = sub { my $loc = $locn->( @_ ); $loc->[0] = $loc->[3]; return $loc; }; my $dumper = PPI::Dumper->new( $elem->ppi(), map { $_ => $self->{$_} } qw{ indent locations }, ); return $dumper->list(); } sub _content { my ( $self, $elem, $dflt ) = @_; defined $dflt or $dflt = ''; defined $elem or return $dflt; if ( ARRAY_REF eq ref $elem ) { my $rslt = join '', map { $self->_content( $_ ) } grep { ! $self->{significant} || $_->significant() } @{ $elem }; return $rslt eq '' ? $dflt : $rslt; } blessed( $elem ) or return $dflt; return $self->encode( $elem->content() ); } sub _tokens_dump { my ( $self, $elem, $depth ) = @_; not $self->{significant} or $elem->significant() or return; my @rslt; foreach my $token ( $elem->tokens() ) { not $self->{significant} or $token->significant() or next; push @rslt, $token->__PPIX_DUMPER__dump( $self, $depth ); } return @rslt; } sub _format_default_modifiers { my ( $self, $subr, $elem ) = @_; my @arg = $self->_safe( $elem ); foreach my $attr ( qw{ default_modifiers parse strict } ) { defined ( my $val = $self->{$attr} ) or next; ARRAY_REF eq ref $val and not @{ $val } and next; push @arg, "$attr => @{[ $self->_safe( $val ) ]}"; } return sprintf '%-8s( %s );', $subr, join ', ', @arg; } sub _format_matcher_dump { my ( undef, $elem ) = @_; my $value = $elem->is_matcher(); return sprintf 'is_matcher=%s', $value ? 'true' : defined $value ? 'false' : 'undef'; } sub _format_modifiers_dump { my ( undef, $elem ) = @_; # Invocant unused my %mods = $elem->modifiers(); my @accum; $mods{match_semantics} and push @accum, 'match_semantics=' . delete $mods{match_semantics}; foreach my $modifier ( sort keys %mods ) { push @accum, $mods{$modifier} ? $modifier : "-$modifier"; } @accum and return join ' ', @accum; return; } sub _tokens_test { my ( $self, $elem ) = @_; not $self->{significant} or $elem->significant() or return; my @tokens = $elem->tokens(); my @rslt = ( $self->_format_default_modifiers( tokenize => $elem ), sprintf( 'count ( %d );', scalar @tokens ), ); my $inx = 0; foreach my $token ( @tokens ) { not $self->{significant} or $token->significant() or next; push @rslt, $token->__PPIX_DUMPER__test( $self, $inx++ ); } return @rslt; } sub PPIx::Regexp::Element::__PPIX_DUMPER__dump_explanation { my ( $self, undef, $line ) = @_; # $dumper unused my @expl = $self->explain() or return $line; 1 == @expl and return "$line\t$expl[0]"; wantarray or return sprintf "%s\t%s", $line, join '; ', @expl; ( my $blank = $line ) =~ s/\S/ /smxg; my @rslt; foreach my $splain ( @expl ) { push @rslt, "$line\t$splain"; $line = $blank; } return @rslt; } sub PPIx::Regexp::__PPIX_DUMPER__test { my ( $self, $dumper ) = @_; $dumper->{tokens} and return $dumper->_tokens_test( $self ); not $dumper->{significant} or $self->significant() or return; # my $parse = 'parse ( ' . $dumper->_safe( $self ) . ' );'; my $parse = $dumper->_format_default_modifiers( parse => $self ); my $fail = 'value ( failures => [], ' . $self->failures() . ' );'; # Note that we can not use SUPER in the following because SUPER goes # by the current package, not by the class of the object. my @rslt = PPIx::Regexp::Node::__PPIX_DUMPER__test( $self, $dumper ); # Get rid of the empty choose(); shift @rslt; return ( $parse, $fail, @rslt ); } sub PPIx::Regexp::Node::__PPIX_DUMPER__dump { my ( $self, $dumper, $depth ) = @_; $depth ||= 0; $dumper->{tokens} and return $dumper->_tokens_dump( $self, $depth ); not $dumper->{significant} or $self->significant() or return; my @rslt = ( ref $self ); $dumper->{short} and $rslt[0] =~ s/ \A PPIx::Regexp:: //smx; $self->isa( 'PPIx::Regexp' ) and $rslt[-1] .= $dumper->{verbose} ? sprintf "\tfailures=%d\tmax_capture_number=%d", $self->failures(), $self->max_capture_number() : sprintf "\tfailures=%d", $self->failures(); substr $rslt[0], 0, 0, ' ' x ( $depth * $dumper->{indent} ); $dumper->{locations} and substr $rslt[0], 0, 0, ' ' x LOCATION_WIDTH; $dumper->{perl_version} and $rslt[-1] .= "\t" . $dumper->_perl_version( $self ); if ( defined ( my $err = $self->error() ) ) { $rslt[-1] .= "\t$err"; } else { $dumper->{explain} and push @rslt, $self->__PPIX_DUMPER__dump_explanation( $dumper, pop @rslt ); } $depth++; foreach my $elem ( $self->children() ) { push @rslt, $elem->__PPIX_DUMPER__dump( $dumper, $depth ); } return @rslt; } sub PPIx::Regexp::Node::__PPIX_DUMPER__test { my ( $self, $dumper ) = @_; not $dumper->{significant} or $self->significant() or return; my @rslt; @rslt = ( 'choose ( ' . $dumper->__nav( $self->nav() ) . ' );', 'klass ( ' . $dumper->_safe( ref $self ) . ' );', 'count ( ' . scalar $self->children() . ' );', ); if ( defined( my $err = $self->error() ) ) { push @rslt, 'error ( ' . $dumper->_safe( $err ) . ' );'; } if ( $dumper->{perl_version} ) { foreach my $method ( qw{ perl_version_introduced perl_version_removed } ) { push @rslt, "value ( $method => [], " . $dumper->_safe_version( $self->$method() ) . ' );'; } } foreach my $elem ( $self->children() ) { push @rslt, $elem->__PPIX_DUMPER__test( $dumper ); } return @rslt; } sub _format_value { my ( $val ) = @_; defined $val or return 'undef'; $val =~ m/ \A [0-9]+ \z /smx and return $val; $val =~ s/ (?= [\\"] ) /\\/smxg; return qq{"$val"}; } { my %dflt = ( start => '???', type => '', finish => '???', ); sub PPIx::Regexp::Structure::__PPIX_DUMPER__dump { my ( $self, $dumper, $depth ) = @_; $depth ||= 0; not $dumper->{significant} or $self->significant() or return; my @delim; foreach my $method ( qw{ start type finish } ) { my @elem = $self->$method(); push @delim, @elem ? $dumper->_content( \@elem ) : $dflt{$method}; } my @rslt = ( ref $self, "$delim[0]$delim[1] ... $delim[2]" ); $dumper->{short} and $rslt[0] =~ s/ \A PPIx::Regexp:: //smx; substr $rslt[0], 0, 0, ' ' x ( $depth * $dumper->{indent} ); $dumper->{locations} and substr $rslt[0], 0, 0, ' ' x LOCATION_WIDTH; $dumper->{perl_version} and push @rslt, $dumper->_perl_version( $self ); if ( $dumper->{verbose} ) { foreach my $method ( qw{ number name max_capture_number } ) { $self->can( $method ) or next; push @rslt, sprintf '%s=%s', $method, _format_value( $self->$method() ); } foreach my $method ( qw{ can_be_quantified is_quantifier } ) { ## is_case_sensitive $self->can( $method ) or next; $self->$method() and push @rslt, $method; } $self->isa( 'PPIx::Regexp::Structure::Modifier' ) and push @rslt, $dumper->_format_modifiers_dump( $self->type( 0 ) ); push @rslt, $dumper->_format_matcher_dump( $self ); } foreach my $method ( 'start', undef, 'finish' ) { my $ele = defined $method ? $self->$method() : $self or next; if ( defined ( my $err = $ele->error() ) ) { push @rslt, $err; } } @rslt = ( join "\t", @rslt ); $dumper->{explain} and not defined $self->error() and push @rslt, $self->__PPIX_DUMPER__dump_explanation( $dumper, pop @rslt ); $depth++; foreach my $elem ( $self->children() ) { push @rslt, $elem->__PPIX_DUMPER__dump( $dumper, $depth ); } return @rslt; } } sub PPIx::Regexp::Structure::__PPIX_DUMPER__test { my ( $self, $dumper ) = @_; not $dumper->{significant} or $self->significant() or return; my @nav = $self->nav(); my @rslt = ( 'choose ( ' . $dumper->__nav( @nav ) . ' );', 'klass ( ' . $dumper->_safe( ref $self ) . ' );', 'count ( ' . scalar $self->children() . ' );', ); if ( $dumper->{verbose} ) { foreach my $method ( qw{ number name } ) { $self->can( $method ) or next; push @rslt, 'value ( ' . $method . ' => [], ' . $dumper->_safe( $self->$method() ) . ' );'; } } foreach my $method ( qw{ start type finish } ) { my @eles = $self->$method(); push @rslt, 'choose ( ' . $dumper->__nav( @nav, $method, [] ) . ' );', 'count ( ' . scalar @eles . ' );'; foreach my $inx ( 0 .. $#eles ) { my $elem = $eles[$inx]; push @rslt, 'choose ( ' . $dumper->__nav( @nav, $method, $inx ) . ' );', 'klass ( ' . $dumper->_safe( ref $elem || $elem ) . ' );', 'content ( ' . $dumper->_safe( $elem ) . ' );'; } } foreach my $elem ( $self->children() ) { push @rslt, $elem->__PPIX_DUMPER__test( $dumper ); } return @rslt; } sub PPIx::Regexp::Tokenizer::__PPIX_DUMPER__dump { my ( $self, $dumper, $depth ) = @_; $depth ||= 0; return $dumper->_tokens_dump( $self, $depth ); } sub PPIx::Regexp::Tokenizer::__PPIX_DUMPER__test { my ( $self, $dumper ) = @_; return $dumper->_tokens_test( $self ); } sub PPIx::Regexp::Token::__PPIX_DUMPER__dump { my ( $self, $dumper, $depth ) = @_; $depth ||= 0; not $dumper->{significant} or $self->significant() or return; my @rslt = ( ref $self, $dumper->_safe( $self ) ); $dumper->{short} and $rslt[0] =~ s/ \A PPIx::Regexp:: //smx; substr $rslt[0], 0, 0, ' ' x ( $depth * $dumper->{indent} ); $dumper->{locations} and substr $rslt[0], 0, 0, sprintf '[ % 4d, % 3d, % 3d ] ', $self->logical_line_number(), $self->column_number(), $self->visual_column_number(); my @ppi; @ppi = $dumper->_ppi( $self ) and shift @ppi; # Ignore PPI::Document foreach ( @ppi ) { if ( $dumper->{locations} ) { s/ ( [0-9]+ \s+ \] ) /$1 /smxg or substr $_, 0, 0, ' '; } else { substr $_, 0, 0, ' '; } } $dumper->{perl_version} and push @rslt, $dumper->_perl_version( $self ); if ( defined( my $err = $self->error() ) ) { return join "\t", @rslt, $err; } else { if ( $dumper->{ordinal} && $self->can( 'ordinal' ) && defined ( my $ord = $self->ordinal() ) ) { push @rslt, sprintf '0x%02x', $ord; } if ( $dumper->{verbose} ) { if ( $self->isa( 'PPIx::Regexp::Token::Reference' ) ) { foreach my $method ( qw{ absolute name number } ) { defined( my $val = $self->$method() ) or next; push @rslt, "$method=$val"; } } foreach my $method ( qw{ significant can_be_quantified is_quantifier } ) { ## is_case_sensitive $self->can( $method ) and $self->$method() and push @rslt, $method; } $self->can( 'ppi' ) and push @rslt, $self->ppi()->content(); if ( $self->isa( 'PPIx::Regexp::Token::Modifier' ) || $self->isa( 'PPIx::Regexp::Token::GroupType::Modifier' ) ) { push @rslt, $dumper->_format_modifiers_dump( $self ); } push @rslt, $dumper->_format_matcher_dump( $self ); } @rslt = ( join "\t", @rslt ); $dumper->{explain} and push @rslt, $self->__PPIX_DUMPER__dump_explanation( $dumper, pop @rslt ); push @rslt, @ppi; return @rslt; } } sub PPIx::Regexp::Token::__PPIX_DUMPER__test { my ( $self, $dumper, @nav ) = @_; not $dumper->{significant} or $self->significant() or return; @nav or @nav = $self->nav(); my @rslt = ( 'choose ( ' . join(', ', $dumper->__nav( @nav ) ) . ' );', 'klass ( ' . $dumper->_safe( ref $self ) . ' );', 'content ( ' . $dumper->_safe( $self ) . ' );', ); if ( defined( my $err = $self->error() ) ) { push @rslt, 'error ( ' . $dumper->_safe( $err ) . ' );'; } else { if ( $dumper->{perl_version} ) { foreach my $method ( qw{ perl_version_introduced perl_version_removed } ) { push @rslt, "value ( $method => [], " . $dumper->_safe_version( $self->$method() ) . ' );'; } } if ( $dumper->{verbose} ) { foreach my $method ( qw{ significant can_be_quantified is_quantifier } ) { ## is_case_sensitive $self->can( $method ) or next; push @rslt, $self->$method() ? "true ( $method => [] );" : "false ( $method => [] );"; } $self->can( 'ppi' ) and push @rslt, "value ( ppi => [], " . $dumper->_safe( $self->ppi() ) . ' );'; } } return @rslt; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Element.pm000444000765000024 7215714151156043 17616 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Element - Base of the PPIx::Regexp hierarchy. =head1 SYNOPSIS No user-serviceable parts inside. =head1 INHERITANCE C is not descended from any other class. C is the parent of L and L. =head1 DESCRIPTION This class is the base of the L object hierarchy. It provides the same kind of navigational functionality that is provided by L. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Element; use strict; use warnings; use 5.006; use Carp; use List::Util qw{ first max min }; use PPIx::Regexp::Util qw{ __instance }; use Scalar::Util qw{ refaddr weaken }; use PPIx::Regexp::Constant qw{ FALSE LITERAL_LEFT_CURLY_REMOVED_PHASE_1 LOCATION_LINE LOCATION_CHARACTER LOCATION_COLUMN LOCATION_LOGICAL_LINE LOCATION_LOGICAL_FILE MINIMUM_PERL TOKEN_UNKNOWN TRUE @CARP_NOT }; our $VERSION = '0.082'; =head2 accepts_perl $token->accepts_perl( '5.020' ) and say 'This works under Perl 5.20'; This method returns a true value if the token is acceptable under the specified version of Perl, and a false value otherwise. Unless the token (or its contents) have been equivocated on, the result is simply what you would expect based on testing the results of L and L versus the given Perl version number. This method was added in version 0.051_01. =cut sub accepts_perl { my ( $self, $version ) = @_; foreach my $check ( $self->__perl_requirements() ) { $version < $check->{introduced} and next; defined $check->{removed} and $version >= $check->{removed} and next; return TRUE; } return FALSE; } # Return the Perl requirements, constructing if necessary. The # requirements are simply an array of hashes containing keys: # {introduced} - The Perl version introduced; # {removed} - The Perl version removed (or undef) # The requirements are evaluated by iterating through the array, # returning a true value if the version of Perl being tested falls # inside any of the half-open (on the right) intervals. sub __perl_requirements { my ( $self ) = @_; return @{ $self->{perl_requirements} ||= [ $self->__perl_requirements_setup() ] }; } # Construct the array returned by __perl_requirements(). sub __perl_requirements_setup { my ( $self ) = @_; return { introduced => $self->perl_version_introduced(), removed => $self->perl_version_removed(), }; } =head2 ancestor_of This method returns true if the object is an ancestor of the argument, and false otherwise. By the definition of this method, C<$self> is its own ancestor. =cut sub ancestor_of { my ( $self, $elem ) = @_; __instance( $elem, __PACKAGE__ ) or return; my $addr = refaddr( $self ); while ( $addr != refaddr( $elem ) ) { $elem = $elem->_parent() or return; } return 1; } =head2 can_be_quantified $token->can_be_quantified() and print "This element can be quantified.\n"; This method returns true if the element can be quantified. =cut sub can_be_quantified { return 1; } =head2 class This method returns the class name of the element. It is the same as C. =cut sub class { my ( $self ) = @_; return ref $self; } =head2 column_number This method returns the column number of the first character in the element, or C if that can not be determined. =cut sub column_number { my ( $self ) = @_; return ( $self->location() || [] )->[LOCATION_CHARACTER]; } =head2 comment This method returns true if the element is a comment and false otherwise. =cut sub comment { return; } =head2 content This method returns the content of the element. =cut sub content { return; } =head2 descendant_of This method returns true if the object is a descendant of the argument, and false otherwise. By the definition of this method, C<$self> is its own descendant. =cut sub descendant_of { my ( $self, $node ) = @_; __instance( $node, __PACKAGE__ ) or return; return $node->ancestor_of( $self ); } =head2 explain This method returns a brief explanation of what the element does. The return will be either a string or C in scalar context, but may be multiple values or an empty array in list context. This method should be considered experimental. What it returns may change without notice as my understanding of what all the pieces/parts of a Perl regular expression evolves. The worst case is that it will prove entirely infeasible to implement satisfactorily, in which case it will be put through a deprecation cycle and retracted. =cut sub explain { my ( $self ) = @_; defined $self->{explanation} and return $self->{explanation}; my $explanation = $self->__explanation(); my $content = $self->content(); if ( my $main = $self->main_structure() ) { my $delim = $main->delimiters(); $delim = qr{ \\ (?= [\Q$delim\E] ) }smx; $content =~ s/$delim//smxg; } if ( defined( my $splain = $explanation->{$content} ) ) { return $splain; } return $self->__no_explanation(); } # Return explanation hash sub __explanation { $PPIx::Regexp::NO_EXPLANATION_FATAL and confess 'Neither explain() nor __explanation() overridden'; return {}; } # Called if no explanation available sub __no_explanation { ## my ( $self ) = @_; # Invocant unused my $msg = sprintf q; $PPIx::Regexp::NO_EXPLANATION_FATAL and confess $msg; return $msg; } =head2 error say $token->error(); If an element is one of the classes that represents a parse error, this method B return a brief message saying why. Otherwise it will return C. =cut sub error { my ( $self ) = @_; return $self->{error}; } =begin comment =head2 first_element This method throws an exception saying that it must be overridden. =end comment =cut sub first_element { confess 'Bug - first_element must be overridden'; } =begin comment =head2 first_token This method throws an exception saying that it must be overridden. =end comment =cut sub first_token { confess 'Bug - first_token must be overridden'; } =head2 is_matcher This method reports on whether the element potentially matches something. Possible returns are a true value if it does, a false (but defined) value if it does not, or C if this can not be determined. The idea is to classify elements based on whether they potentially match something in the target string. This method is overridden to return C in L, L, and L. This method is overridden to return a true value in L, L, L, and L. For L, this method is overridden to return a value computed from the node's children. For anything else this method returns a false (but defined) value. =cut sub is_matcher { return 0; } # NOTE retracted this as a public method until I can investigate whether # the tokenizer can actually produce nested assertions. #=head2 in_assertion # #This method returns an array of assertions that contain the element, #most-local first. For the purpose of this method, a look-around #structure does not contain itself. If called in scalar context you get #the size of the array. # #This method was added in version 0.075_01. # #=cut sub __in_assertion { my ( $self ) = @_; my $elem = $self; my @assertions; while ( $elem = $elem->parent() ) { $elem->isa( 'PPIx::Regexp::Structure::Assertion' ) and push @assertions, $elem; } return @assertions; } # Convenience method that returns the number of look-behind # assertions that contain the current element. This is really only # here so it can be shared between PPIx::Regexp::Token::Quantifier # and PPIx::Regexp::Structure::Quantifier sub __in_look_behind { my ( $self ) = @_; my @look_behind; foreach my $assertion ( $self->__in_assertion() ) { $assertion->is_look_ahead() and next; push @look_behind, $assertion; } return @look_behind; } =head2 in_regex_set This method returns a true value if the invocant is contained in an extended bracketed character class (also known as a regex set), and a false value otherwise. This method returns true if the invocant is a L. =cut sub in_regex_set { my ( $self ) = @_; my $ele = $self; while ( 1 ) { $ele->isa( 'PPIx::Regexp::Structure::RegexSet' ) and return 1; $ele = $ele->parent() or last; } return 0; } =head2 is_quantifier $token->is_quantifier() and print "This element is a quantifier.\n"; This method returns true if the element is a quantifier. You can not tell this from the element's class, because a right curly bracket may represent a quantifier for the purposes of figuring out whether a greediness token is possible. =cut sub is_quantifier { return; } =begin comment =head2 last_element This method throws an exception saying that it must be overridden. =end comment =cut sub last_element { confess 'Bug - last_element must be overridden'; } =begin comment =head2 last_token This method throws an exception saying that it must be overridden. =end comment =cut sub last_token { confess 'Bug - last_token must be overridden'; } =head2 line_number This method returns the line number of the first character in the element, or C if that can not be determined. =cut sub line_number { my ( $self ) = @_; return ( $self->location() || [] )->[LOCATION_LINE]; } =head2 location This method returns a reference to an array describing the position of the element in the regular expression, or C if locations were not indexed. The array is compatible with the corresponding L method. =cut sub location { my ( $self ) = @_; return $self->{location} ? [ @{ $self->{location} } ] : undef; } =pod =head2 logical_filename This method returns the logical file name (taking C<#line> directives into account) of the file containing first character in the element, or C if that can not be determined. =cut sub logical_filename { my ( $self ) = @_; return ( $self->location() || [] )->[LOCATION_LOGICAL_FILE]; } =head2 logical_line_number This method returns the logical line number (taking C<#line> directives into account) of the first character in the element, or C if that can not be determined. =cut sub logical_line_number { my ( $self ) = @_; return ( $self->location() || [] )->[LOCATION_LOGICAL_LINE]; } =head2 main_structure This method returns the L that contains the element. In practice this will be a L or a L, If the element is not contained in any such structure, C is returned. This will happen if the element is a L or one of its immediate children. =cut sub main_structure { my ( $self ) = @_; while ( $self = $self->parent() and not $self->isa( 'PPIx::Regexp::Structure::Main' ) ) { } return $self; } =head2 modifier_asserted $token->modifier_asserted( 'i' ) and print "Matched without regard to case.\n"; This method returns true if the given modifier is in effect for the element, and false otherwise. What it does is to walk backwards from the element until it finds a modifier object that specifies the modifier, whether asserted or negated. and returns the specified value. If nobody specifies the modifier, it returns C. This method will not work reliably if called on tokenizer output. =cut sub modifier_asserted { my ( $self, $modifier ) = @_; defined $modifier or croak 'Modifier must be defined'; my $elem = $self; while ( $elem ) { if ( $elem->can( '__ducktype_modifier_asserted' ) ) { my $val; defined( $val = $elem->__ducktype_modifier_asserted( $modifier ) ) and return $val; } if ( my $prev = $elem->sprevious_sibling() ) { $elem = $prev; } else { $elem = $elem->parent(); } } return; } =head2 next_element This method returns the next element, or nothing if there is none. Unlike L, this will cross from the content of a structure into the elements that define the structure, or vice versa. =cut sub next_element { my ( $self ) = @_; my $parent = $self->_parent() or return; my $inx = $self->__my_inx(); return ( $parent->elements() )[ $inx + 1 ]; } =head2 next_sibling This method returns the element's next sibling, or nothing if there is none. =cut sub next_sibling { my ( $self ) = @_; my ( $method, $inx ) = $self->__my_nav() or return; return $self->_parent()->$method( $inx + 1 ); } =head2 next_token This method returns the next token, or nothing if there is none. Unlike L, this will walk the parse tree. =cut sub next_token { my ( $self ) = @_; if ( my $next = $self->next_element() ) { return $next->first_token(); } elsif ( my $parent = $self->parent() ) { return $parent->next_token(); } else { return; } } =head2 parent This method returns the parent of the element, or undef if there is none. =cut sub parent { my ( $self ) = @_; return $self->_parent(); } =head2 perl_version_introduced This method returns the version of Perl in which the element was introduced. This will be at least 5.000. Before 5.006 I am relying on the F, F, and F documentation, since I have been unable to build earlier Perls. Since I have found no documentation before 5.003, I assume that anything found in 5.003 is also in 5.000. Since this all depends on my ability to read and understand masses of documentation, the results of this method should be viewed with caution, if not downright skepticism. There are also cases which are ambiguous in various ways. For those see the L documentation, particularly L. Very occasionally, a construct will be removed and then added back. If this happens, this method will return the B version in which the construct appeared. For the known instances of this, see the L documentation, particularly L. =cut sub perl_version_introduced { return MINIMUM_PERL; } =head2 perl_version_removed This method returns the version of Perl in which the element was removed. If the element is still valid the return is C. All the I to L apply here also, though perhaps less severely since although many features have been introduced since 5.0, few have been removed. Very occasionally, a construct will be removed and then added back. If this happens, this method will return the C if the construct is present in the highest-numbered version of Perl (whether production or development), or the version after the highest-numbered version in which it appeared otherwise. For the known instances of this, see the L documentation, particularly L. =cut sub perl_version_removed { return undef; ## no critic (ProhibitExplicitReturnUndef) } =head2 previous_element This method returns the previous element, or nothing if there is none. Unlike L, this will cross from the content of a structure into the elements that define the structure, or vice versa. =cut sub previous_element { my ( $self ) = @_; my $parent = $self->_parent() or return; my $inx = $self->__my_inx() or return; return ( $parent->elements() )[ $inx - 1 ]; } =head2 previous_sibling This method returns the element's previous sibling, or nothing if there is none. This method is analogous to the same-named L method, in that it will not cross from the content of a structure into the elements that define the structure. =cut sub previous_sibling { my ( $self ) = @_; my ( $method, $inx ) = $self->__my_nav() or return; $inx or return; return $self->_parent()->$method( $inx - 1 ); } =head2 previous_token This method returns the previous token, or nothing if there is none. Unlike L, this will walk the parse tree. =cut sub previous_token { my ( $self ) = @_; if ( my $previous = $self->previous_element() ) { return $previous->last_token(); } elsif ( my $parent = $self->parent() ) { return $parent->previous_token(); } else { return; } } =head2 remove_insignificant This method returns a new object manufactured from the invocant, but containing only elements for which C<< $elem->significant() >> returns a true value. If you call this method on a L you will get back a deep clone, but without the insignificant elements. If you call this method on any other L class you will get back either the invocant or nothing. This may change to a clone of the invocant or nothing if unforseen problems arise with returning the invocant, or if objects become mutable (unlikely, but not impossible.) =cut sub remove_insignificant { my ( $self ) = @_; $self->significant() and return $self; return; } =head2 requirements_for_perl say $token->requirements_for_perl(); This method returns a string representing the Perl requirements for a given module. This should only be used for informational purposes, as the format of the string may be subject to change. At the moment, the returns may be: version <= $] version <= $] < version two or more of the above joined by '||' ! $] The last means that, although all the components of the regular expression can be compiled by B version of Perl, there is no version that will compile all of them. I reiterate: the returned string may be subject to change, maybe without warning. This method was added in version 0.051_01. =cut sub requirements_for_perl { my ( $self ) = @_; my @req; foreach my $r ( @{ $self->__structured_requirements_for_perl() || [] } ) { push @req, defined $r->{removed} ? "$r->{introduced} <= \$] < $r->{removed}" : "$r->{introduced} <= \$]"; } @req or return '! $]'; return join ' || ', @req; } =head2 scontent This method returns the significant content of the element. That is, if called on the parse of C<'/ f u b a r /x'>, it returns C<'/fubar/x'>. If the invocant contains no insignificant elements, it is the same as L. If called on an insignificant element, it returns nothing -- that is, C in scalar context, and an empty list in list context. This method was inspired by jb's question on Perl Monks about stripping comments and white space from a regular expression: L This method was added in version 0.053_01 =cut sub scontent { return; } =head2 significant This method returns true if the element is significant and false otherwise. =cut sub significant { return 1; } =head2 snext_element This method returns the next significant element, or nothing if there is none. Unlike L, this will cross from the content of a structure into the elements that define the structure, or vice versa. =cut sub snext_element { my ( $self ) = @_; my $inx = $self->__my_inx(); my $parent = $self->_parent() or return; my @elem = $parent->elements(); while ( 1 ) { $inx++; $elem[$inx] or last; $elem[$inx]->significant() and return $elem[$inx]; } return; } =head2 snext_sibling This method returns the element's next significant sibling, or nothing if there is none. This method is analogous to the same-named L method, in that it will not cross from the content of a structure into the elements that define the structure. =cut sub snext_sibling { my ( $self ) = @_; my $sib = $self; while ( defined ( $sib = $sib->next_sibling() ) ) { $sib->significant() and return $sib; } return; } =head2 sprevious_element This method returns the previous significant element, or nothing if there is none. Unlike L, this will cross from the content of a structure into the elements that define the structure, or vice versa. =cut sub sprevious_element { my ( $self ) = @_; my $inx = $self->__my_inx() or return; my $parent = $self->_parent() or return; my @elem = $parent->elements(); while ( $inx ) { $elem[--$inx]->significant() and return $elem[$inx]; } return; } =head2 sprevious_sibling This method returns the element's previous significant sibling, or nothing if there is none. This method is analogous to the same-named L method, in that it will not cross from the content of a structure into the elements that define the structure. =cut sub sprevious_sibling { my ( $self ) = @_; my $sib = $self; while ( defined ( $sib = $sib->previous_sibling() ) ) { $sib->significant() and return $sib; } return; } =head2 statement This method returns the L that contains this element, or nothing if the statement can not be determined. In general this method will return something only under the following conditions: =over =item * The element is contained in a L object; =item * That object was initialized from a L; =item * The L is contained in a statement. =back =cut sub statement { my ( $self ) = @_; my $top = $self->top() or return; $top->can( 'source' ) or return; my $source = $top->source() or return; $source->can( 'statement' ) or return; return $source->statement(); } # NOTE: This method is to be used ONLY for requirements_for_perl(). I # _may_ eventually expose it, but at the moment I do not consider it # stable. The exposure would be # sub structured_requirements_for_perl { # my ( $self ) = @_; # return $self->__structured_requirements_for_perl(); # } # The return ia a reference to an array of hashes. Each hash contains # key {introduced} (the version the element was introduced) and MAYBE # key {removed} (the version the element was removed). There may be more # than one such, and their ranges will not overlap. sub __structured_requirements_for_perl { my ( $self, $rslt ) = @_; $rslt ||= $self->__structured_requirements_for_any_perl(); my @merged; foreach my $left ( $self->__perl_requirements() ) { foreach my $right ( @{ $rslt } ) { my $min = max( $left->{introduced}, $right->{introduced} ); my $max = defined $left->{removed} ? defined $right->{removed} ? min( $left->{removed}, $right->{removed} ) : $left->{removed} : $right->{removed}; defined $max and $max <= $min and next; push @merged, { introduced => $min, removed => $max, }; } } @{ $rslt } = @merged; return $rslt; } # NOTE: This method is to be used ONLY to initialize # __structured_requirements_for_perl(). It returns a structure that # matches any Perl. sub __structured_requirements_for_any_perl { return [ { introduced => MINIMUM_PERL, removed => undef, }, ]; } =head2 tokens This method returns all tokens contained in the element. =cut sub tokens { my ( $self ) = @_; return $self; } =head2 top This method returns the top of the hierarchy. =cut sub top { my ( $self ) = @_; my $kid = $self; while ( defined ( my $parent = $kid->_parent() ) ) { $kid = $parent; } return $kid; } =head2 unescaped_content This method returns the content of the element, unescaped. =cut sub unescaped_content { return; } =head2 visual_column_number This method returns the visual column number (taking tabs into account) of the first character in the element, or C if that can not be determined. =cut sub visual_column_number { my ( $self ) = @_; return ( $self->location() || [] )->[LOCATION_COLUMN]; } =head2 whitespace This method returns true if the element is whitespace and false otherwise. =cut sub whitespace { return; } =head2 nav This method returns navigation information from the top of the hierarchy to this node. The return is a list of names of methods and references to their argument lists. The idea is that given C<$elem> which is somewhere under C<$top>, my @nav = $elem->nav(); my $obj = $top; while ( @nav ) { my $method = shift @nav; my $args = shift @nav; $obj = $obj->$method( @{ $args } ) or die; } # At this point, $obj should contain the same object # as $elem. =cut sub nav { my ( $self ) = @_; __instance( $self, __PACKAGE__ ) or return; # We do not use $self->parent() here because PPIx::Regexp overrides # this to return the (possibly) PPI object that initiated us. my $parent = $self->_parent() or return; return ( $parent->nav(), $parent->__nav( $self ) ); } # Find our index among the parents children. If not found, just return. # Unlike __my_nav(), this just returns an index, which is appropriate # for ->element( $inx ), or would be if element() existed. sub __my_inx { my ( $self ) = @_; my $parent = $self->_parent() or return; my $addr = refaddr( $self ); my @elem = $parent->elements(); return first { refaddr( $elem[$_] ) == $addr } 0 .. $#elem; } # Find our location and index among the parent's children. If not found, # just returns. { my %method_map = ( children => 'child', ); sub __my_nav { my ( $self ) = @_; my $parent = $self->_parent() or return; my $addr = refaddr( $self ); foreach my $method ( qw{ children start type finish } ) { $parent->can( $method ) or next; my @elem = $parent->$method(); defined( my $inx = first { refaddr( $elem[$_] ) == $addr } 0 .. $#elem ) or next; return ( $method_map{$method} || $method, $inx ); } return; } } { my %parent; # no-argument form returns the parent; one-argument sets it. sub _parent { my ( $self, @arg ) = @_; my $addr = refaddr( $self ); if ( @arg ) { my $parent = shift @arg; if ( defined $parent ) { __instance( $parent, __PACKAGE__ ) or return; weaken( $parent{$addr} = $parent ); } else { delete $parent{$addr}; } } return $parent{$addr}; } sub __parent_keys { return scalar keys %parent; } } # Bless into TOKEN_UNKNOWN, record error message, return 1. sub __error { my ( $self, $msg ) = @_; defined $msg or $msg = 'Was ' . ref $self; $self->{error} = $msg; bless $self, TOKEN_UNKNOWN; return 1; } # This huge kluge is required by # https://rt.perl.org/Ticket/Display.html?id=128213 which means the # deprecation will be done in at least two separate phases. It exists # for the use of PPIx::Regexp::Token::Literal->perl_version_removed, and # MUST NOT be called by any other code. # Note that the perldelta for 5.25.1 and 5.26.0 do not acknowledge tha # phased deprecation, and pretend that everything was done on the phase # 1 schedule. This appears to be deliberate per # https://rt.perl.org/Ticket/Display.html?id=131352 sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_REMOVED_PHASE_1; } # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( undef, $number ) = @_; # Invocant unused return $number; } # Called by the lexer to rebless sub __PPIX_ELEM__rebless { my ( $class, $self ) = @_; # %arg unused $self ||= {}; bless $self, $class; delete $self->{error}; defined $self->{error} and return 1; delete $self->{error}; return 0; } sub DESTROY { $_[0]->_parent( undef ); return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Lexer.pm000444000765000024 3750314151156043 17300 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Lexer - Assemble tokenizer output. =head1 SYNOPSIS use PPIx::Regexp::Lexer; use PPIx::Regexp::Dumper; my $lex = PPIx::Regexp::Lexer->new('qr{foo}smx'); my $dmp = PPIx::Regexp::Dumper->new( $lex ); $dmp->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class takes the token stream generated by L and generates the parse tree. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Lexer; use strict; use warnings; use base qw{ PPIx::Regexp::Support }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ ARRAY_REF TOKEN_LITERAL TOKEN_UNKNOWN @CARP_NOT }; use PPIx::Regexp::Node::Range (); use PPIx::Regexp::Node::Unknown (); use PPIx::Regexp::Structure (); use PPIx::Regexp::Structure::Assertion (); use PPIx::Regexp::Structure::Atomic_Script_Run (); use PPIx::Regexp::Structure::BranchReset (); use PPIx::Regexp::Structure::Code (); use PPIx::Regexp::Structure::Capture (); use PPIx::Regexp::Structure::CharClass (); use PPIx::Regexp::Structure::Subexpression (); use PPIx::Regexp::Structure::Main (); use PPIx::Regexp::Structure::Modifier (); use PPIx::Regexp::Structure::NamedCapture (); use PPIx::Regexp::Structure::Quantifier (); use PPIx::Regexp::Structure::Regexp (); use PPIx::Regexp::Structure::RegexSet (); use PPIx::Regexp::Structure::Replacement (); use PPIx::Regexp::Structure::Script_Run (); use PPIx::Regexp::Structure::Switch (); use PPIx::Regexp::Structure::Unknown (); use PPIx::Regexp::Token::Unmatched (); use PPIx::Regexp::Tokenizer (); use PPIx::Regexp::Util qw{ __choose_tokenizer_class __instance }; our $VERSION = '0.082'; =head2 new This method instantiates the lexer. It takes as its argument either a L or the text to be parsed. In the latter case the tokenizer is instantiated from the text. Any optional name/value pairs after the first argument are passed to the tokenizer, which interprets them or not as the case may be. =cut { my $errstr; sub new { my ( $class, $tokenizer, %args ) = @_; ref $class and $class = ref $class; unless ( __instance( $tokenizer, 'PPIx::Regexp::Tokenizer' ) ) { my $tokenizer_class = __choose_tokenizer_class( $tokenizer, \%args ) or do { $errstr = 'Data not supported'; return; }; $tokenizer = $tokenizer_class->new( $tokenizer, %args ) or do { $errstr = $tokenizer_class->errstr(); return; }; } my $self = { deferred => [], # Deferred tokens failures => 0, strict => $args{strict}, tokenizer => $tokenizer, }; bless $self, $class; return $self; } sub errstr { return $errstr; } } =head2 errstr This method returns the error string from the last attempt to instantiate a C. If the last attempt succeeded, the error will be C. =cut # Defined above =head2 failures print $lexer->failures(), " parse failures\n"; This method returns the number of parse failures encountered. A parse failure is either a tokenization failure (see L<< PPIx::Regexp::Tokenizer->failures()|PPIx::Regexp::Tokenizer/failures >>) or a structural error. =cut sub failures { my ( $self ) = @_; return $self->{failures}; } =head2 lex This method lexes the tokens in the text, and returns the lexed list of elements. =cut sub lex { my ( $self ) = @_; my @content; $self->{failures} = 0; # Accept everything up to the first delimiter. my $kind; # Initial PPIx::Regexp::Token::Structure { my $token = $self->_get_token() or return $self->_finalize( @content ); $token->isa( 'PPIx::Regexp::Token::Delimiter' ) or do { not $kind and $token->isa( 'PPIx::Regexp::Token::Structure' ) and $kind = $token; push @content, $token; redo; }; $self->_unget_token( $token ); } my ( $part_0_class, $part_1_class ) = $self->{tokenizer}->__part_classes(); # Accept the first delimited structure. push @content, ( my $part_0 = $self->_get_delimited( $part_0_class ) ); # If we are a substitution ... if ( defined $part_1_class ) { # Accept any insignificant stuff. while ( my $token = $self->_get_token() ) { if ( $token->significant() ) { $self->_unget_token( $token ); last; } else { push @content, $token; } } # Figure out if we should expect an opening bracket. my $expect_open_bracket = $self->close_bracket( $part_0->start( 0 ) ) || 0; # Accept the next delimited structure. push @content, $self->_get_delimited( $part_1_class, $expect_open_bracket, ); } # Accept the modifiers (we hope!) plus any trailing white space. while ( my $token = $self->_get_token() ) { push @content, $token; } # Let all the elements finalize themselves, recording any additional # errors as they do so. $self->_finalize( @content ); # If we found a regular expression (and we should have done so) ... if ( $part_0 && $part_0->can( 'max_capture_number' ) ) { # TODO the above line is really ugly. I'm wondering about # string implementations like: # * return a $part_0_class of undef (but that complicates the # lexing of the structure itself); # * hang this logic on the tokenizer somehow (where it seems out # of place) # * hang this logic on PPIx::Regexp::Structure::Regexp and # ::Replacement. # I also need to figure out how to make \n backreferences come # out as literals. Maybe that is a job best done by the # tokenizer. # Retrieve the maximum capture group. my $max_capture = $part_0->max_capture_number(); # Hashify the known capture names my $capture_name = { map { $_ => 1 } $part_0->capture_names(), }; # For all the backreferences found foreach my $elem ( @{ $part_0->find( 'PPIx::Regexp::Token::Backreference' ) || [] } ) { # Rebless them as needed, recording any errors found. $self->{failures} += $elem->__PPIX_LEXER__rebless( capture_name => $capture_name, max_capture => $max_capture, ); } } return @content; } =head2 strict This method returns true or false based on the value of the C<'strict'> argument to C. =cut sub strict { my ( $self ) = @_; return $self->{strict}; } # Finalize the content array, updating the parse failures count as we # go. sub _finalize { my ( $self, @content ) = @_; foreach my $elem ( @content ) { $self->{failures} += $elem->__PPIX_LEXER__finalize( $self ); } defined wantarray and return @content; return; } { my %bracket = ( '{' => '}', '(' => ')', '[' => ']', '(?[' => '])', ## '<' => '>', ); my %unclosed = ( '{' => '_recover_curly', ); sub _get_delimited { my ( $self, $class, $expect_open_bracket ) = @_; defined $expect_open_bracket or $expect_open_bracket = 1; my @rslt; $self->{_rslt} = \@rslt; if ( $expect_open_bracket ) { if ( my $token = $self->_get_token() ) { push @rslt, []; if ( $token->isa( 'PPIx::Regexp::Token::Delimiter' ) ) { push @{ $rslt[-1] }, '', $token; } else { push @{ $rslt[-1] }, '', undef; $self->_unget_token( $token ); } } else { return; } } else { push @rslt, [ '', undef ]; } while ( my $token = $self->_get_token() ) { if ( $token->isa( 'PPIx::Regexp::Token::Delimiter' ) ) { $self->_unget_token( $token ); last; } if ( $token->isa( 'PPIx::Regexp::Token::Structure' ) ) { my $content = $token->content(); if ( my $finish = $bracket{$content} ) { # Open bracket push @rslt, [ $finish, $token ]; } elsif ( $content eq $rslt[-1][0] ) { # Matched close bracket $self->_make_node( $token ); } elsif ( $content ne ')' ) { # If the close bracket is not a parenthesis, it becomes # a literal. TOKEN_LITERAL->__PPIX_ELEM__rebless( $token ); push @{ $rslt[-1] }, $token; } elsif ( $content eq ')' and @rslt > 1 # Ignore enclosing delimiter and my $recover = $unclosed{$rslt[-1][1]->content()} ) { # If the close bracket is a parenthesis and there is a # recovery procedure, we use it. $self->$recover( $token ); } else { # Unmatched close with no recovery. $self->{failures}++; PPIx::Regexp::Token::Unmatched-> __PPIX_ELEM__rebless( $token ); push @{ $rslt[-1] }, $token; } } else { push @{ $rslt[-1] }, $token; } # We have to hand-roll the Range object. if ( __instance( $rslt[-1][-2], 'PPIx::Regexp::Token::Operator' ) && $rslt[-1][-2]->content() eq '-' && $rslt[-1][0] eq ']' # It's a character class ) { my @tokens = splice @{ $rslt[-1] }, -3; push @{ $rslt[-1] }, PPIx::Regexp::Node::Range->__new( @tokens ); } } while ( @rslt > 1 ) { if ( my $recover = $unclosed{$rslt[-1][1]->content()} ) { $self->$recover(); } else { $self->{failures}++; $self->_make_node( undef ); } } if ( @rslt == 1 ) { my @last = @{ pop @rslt }; shift @last; push @last, $self->_get_token(); return $class->__new( @last ); } else { confess "Missing data"; } } } # $token = $self->_get_token(); # # This method returns the next token from the tokenizer. sub _get_token { my ( $self ) = @_; if ( @{ $self->{deferred} } ) { return shift @{ $self->{deferred} }; } my $token = $self->{tokenizer}->next_token() or return; return $token; } { my %handler = ( '(' => '_round', '[' => '_square', '{' => '_curly', '(?[' => '_regex_set', ); sub _make_node { my ( $self, $token ) = @_; my @args = @{ pop @{ $self->{_rslt} } }; shift @args; push @args, $token; my @node; if ( my $method = $handler{ $args[0]->content() } ) { @node = $self->$method( \@args ); } @node or @node = PPIx::Regexp::Structure->__new( @args ); push @{ $self->{_rslt}[-1] }, @node; return; } } # Called as $self->$method( ... ) in _make_node(), above sub _curly { ## no critic (ProhibitUnusedPrivateSubroutines) my ( $self, $args ) = @_; if ( $args->[-1] && $args->[-1]->is_quantifier() ) { # If the tokenizer has marked the right curly as a quantifier, # make the whole thing a quantifier structure. return PPIx::Regexp::Structure::Quantifier->__new( @{ $args } ); } elsif ( $args->[-1] ) { # If there is a right curly but it is not a quantifier, # make both curlys into literals. foreach my $inx ( 0, -1 ) { TOKEN_LITERAL->__PPIX_ELEM__rebless( $args->[$inx] ); } # Try to recover possible quantifiers not recognized because we # thought this was a structure. $self->_recover_curly_quantifiers( $args ); return @{ $args }; } else { # If there is no right curly, just make a generic structure # TODO maybe this should be something else? return PPIx::Regexp::Structure->__new( @{ $args } ); } } # Recover from an unclosed left curly. # Called as $self->$revover( ... ) in _get_delimited, above sub _recover_curly { ## no critic (ProhibitUnusedPrivateSubroutines) my ( $self, $token ) = @_; # Get all the stuff we have accumulated for this curly. my @content = @{ pop @{ $self->{_rslt} } }; # Lose the right bracket, which we have already failed to match. shift @content; # Rebless the left curly appropriately if ( $self->{_rslt}[0][-1]->isa( 'PPIx::Regexp::Token::Assertion' ) && q<\b> eq $self->{_rslt}[0][-1]->content() ) { # If following \b, it becomes an unknown. TOKEN_UNKNOWN->__PPIX_ELEM__rebless( $content[0], error => 'Unterminated bound type', ); } else { # Rebless the left curly to a literal. TOKEN_LITERAL->__PPIX_ELEM__rebless( $content[0] ); } # Try to recover possible quantifiers not recognized because we # thought this was a structure. $self->_recover_curly_quantifiers( \@content ); # Shove the curly and its putative contents into whatever structure # we have going. # The checks are to try to trap things like RT 56864, though on # further reflection it turned out that you could get here with an # empty $self->{_rslt} on things like 'm{)}'. This one did not get # made into an RT ticket, but was fixed by not calling the recovery # code if $self->{_rslt} contained only the enclosing delimiters. ARRAY_REF eq ref $self->{_rslt} or confess 'Programming error - $self->{_rslt} not array ref, ', "parsing '", $self->{tokenizer}->content(), "' at ", $token->content(); @{ $self->{_rslt} } or confess 'Programming error - $self->{_rslt} empty, ', "parsing '", $self->{tokenizer}->content(), "' at ", $token->content(); push @{ $self->{_rslt}[-1] }, @content; # Shove the mismatched delimiter back into the input so we can have # another crack at it. $token and $self->_unget_token( $token ); # We gone. return; } sub _recover_curly_quantifiers { my ( undef, $args ) = @_; # Invocant unused if ( __instance( $args->[0], TOKEN_LITERAL ) && __instance( $args->[1], TOKEN_UNKNOWN ) && PPIx::Regexp::Token::Quantifier->could_be_quantifier( $args->[1]->content() ) ) { PPIx::Regexp::Token::Quantifier-> __PPIX_ELEM__rebless( $args->[1] ); if ( __instance( $args->[2], TOKEN_UNKNOWN ) && PPIx::Regexp::Token::Greediness->could_be_greediness( $args->[2]->content() ) ) { PPIx::Regexp::Token::Greediness ->__PPIX_ELEM__rebless( $args->[2] ); } } return; } sub _in_regex_set { my ( $self ) = @_; foreach my $stack_entry ( reverse @{ $self->{_rslt} } ) { $stack_entry->[0] eq '])' and return 1; } return 0; } # Called as $self->$method( ... ) in _make_node(), above sub _round { ## no critic (ProhibitUnusedPrivateSubroutines) my ( $self, $args ) = @_; # If we're inside a regex set, parens do not capture. $self->_in_regex_set() and return PPIx::Regexp::Structure->__new( @{ $args } ); # If /n is asserted, parens do not capture. $self->{tokenizer}->modifier( 'n' ) and return PPIx::Regexp::Structure->__new( @{ $args } ); # The instantiator will rebless based on the first token if need be. return PPIx::Regexp::Structure::Capture->__new( @{ $args } ); } # Called as $self->$method( ... ) in _make_node(), above sub _square { ## no critic (ProhibitUnusedPrivateSubroutines) my ( undef, $args ) = @_; # Invocant unused return PPIx::Regexp::Structure::CharClass->__new( @{ $args } ); } # Called as $self->$method( ... ) in _make_node(), above sub _regex_set { ## no critic (ProhibitUnusedPrivateSubroutines) my ( undef, $args ) = @_; # Invocant unused return PPIx::Regexp::Structure::RegexSet->__new( @{ $args } ); } # $self->_unget_token( $token ); # # This method caches its argument so that it will be returned by # the next call to C<_get_token()>. If more than one argument is # passed, they will be returned in the order given; that is, # _unget_token/_get_token work like unshift/shift. sub _unget_token { my ( $self, @args ) = @_; unshift @{ $self->{deferred} }, @args; return $self; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Node.pm000444000765000024 3250714151156043 17105 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Node - Represent a container =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(foo)}' )->print(); =head1 INHERITANCE C is a L. C is the parent of L, L and L. =head1 DESCRIPTION This class represents a structural element that contains other classes. It is an abstract class, not instantiated by the lexer. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Node; use strict; use warnings; use base qw{ PPIx::Regexp::Element }; use Carp; use List::Util qw{ max }; use PPIx::Regexp::Constant qw{ CODE_REF FALSE MINIMUM_PERL NODE_UNKNOWN TRUE @CARP_NOT }; use PPIx::Regexp::Util qw{ __instance }; use Scalar::Util qw{ refaddr }; our $VERSION = '0.082'; use constant ELEMENT_UNKNOWN => NODE_UNKNOWN; sub __new { my ( $class, @children ) = @_; foreach my $elem ( @children ) { __instance( $elem, 'PPIx::Regexp::Element' ) or return; } my $self = { children => \@children, }; bless $self, ref $class || $class; foreach my $elem ( @children ) { $elem->_parent( $self ); } return $self; } sub accepts_perl { my ( $self, $version ) = @_; foreach my $elem ( $self->elements() ) { $elem->accepts_perl( $version ) or return FALSE; } return TRUE; } # NOTE: this method is to be used ONLY for requirements_for_perl(). See # PPIx::Regexp::Element for exposure plans. IF it is exposed, that is # where it will be documented. sub __structured_requirements_for_perl { my ( $self, $rslt ) = @_; $rslt ||= $self->__structured_requirements_for_any_perl(); foreach my $elem ( $self->elements() ) { $elem->__structured_requirements_for_perl( $rslt ); } return $rslt; } =head2 child my $kid = $node->child( 0 ); This method returns the child at the given index. The indices start from zero, and negative indices are from the end of the list, so that C<< $node->child( -1 ) >> returns the last child of the node. =cut sub child { my ( $self, $inx ) = @_; defined $inx or $inx = 0; return $self->{children}[$inx]; } =head2 children This method returns the children of the Node. If called in scalar context it returns the number of children. =cut sub children { my ( $self ) = @_; return @{ $self->{children} }; } =head2 contains print $node->contains( $elem ) ? "yes\n" : "no\n"; This method returns true if the given element is contained in the node, or false otherwise. =cut sub contains { my ( $self, $elem ) = @_; __instance( $elem, 'PPIx::Regexp::Element' ) or return; my $addr = refaddr( $self ); while ( $elem = $elem->parent() ) { $addr == refaddr( $elem ) and return 1; } return; } sub content { my ( $self ) = @_; return join( '', map{ $_->content() } $self->elements() ); } =head2 elements This method returns the elements in the Node. For a C proper, it is the same as C. =cut { no warnings qw{ once }; *elements = \&children; # sub slements } =head2 find my $rslt = $node->find( 'PPIx::Regexp::Token::Literal' ); my $rslt = $node->find( 'Token::Literal' ); my $rslt = $node->find( sub { return $_[1]->isa( 'PPIx::Regexp::Token::Literal' ) && $_[1]->ordinal < ord(' '); } ); This method finds things. If given a string as argument, it is assumed to be a class name (possibly without the leading 'PPIx::Regexp::'), and all elements of the given class are found. If given a code reference, that code reference is called once for each element, and passed C<$self> and the element. The code should return true to accept the element, false to reject it, and ( for subclasses of C) C to prevent recursion into the node. If the code throws an exception, you get nothing back from this method. Either way, the return is a reference to the list of things found, a false (but defined) value if nothing was found, or C if an error occurred. =cut sub _find_routine { my ( $want ) = @_; CODE_REF eq ref $want and return $want; ref $want and return; $want =~ m/ \A PPIx::Regexp:: /smx or $want = 'PPIx::Regexp::' . $want; return sub { return __instance( $_[1], $want ) ? 1 : 0; }; } sub find { my ( $self, $want ) = @_; $want = _find_routine( $want ) or return; my @found; # We use a recursion to find what we want. PPI::Node uses an # iteration. foreach my $elem ( $self->elements() ) { my $rslt = eval { $want->( $self, $elem ) } and push @found, $elem; $@ and return; __instance( $elem, 'PPIx::Regexp::Node' ) or next; defined $rslt or next; $rslt = $elem->find( $want ) and push @found, @{ $rslt }; } return @found ? \@found : 0; } =head2 find_parents my $rslt = $node->find_parents( sub { return $_[1]->isa( 'PPIx::Regexp::Token::Operator' ) && $_[1]->content() eq '|'; } ); This convenience method takes the same arguments as C, but instead of the found objects themselves returns their parents. No parent will appear more than once in the output. This method returns a reference to the array of parents if any were found. If no parents were found the return is false but defined. If an error occurred the return is C. =cut sub find_parents { my ( $self, $want ) = @_; my $found; $found = $self->find( $want ) or return $found; my %parents; my @rslt; foreach my $elem ( @{ $found } ) { my $dad = $elem->parent() or next; $parents{ refaddr( $dad ) }++ or push @rslt, $dad; } return \@rslt; } =head2 find_first This method has the same arguments as L, but returns either a reference to the first element found, a false (but defined) value if no elements were found, or C if an error occurred. =cut sub find_first { my ( $self, $want ) = @_; $want = _find_routine( $want ) or return; # We use a recursion to find what we want. PPI::Node uses an # iteration. foreach my $elem ( $self->elements() ) { my $rslt = eval { $want->( $self, $elem ) } and return $elem; $@ and return; __instance( $elem, 'PPIx::Regexp::Node' ) or next; defined $rslt or next; defined( $rslt = $elem->find_first( $want ) ) or return; $rslt and return $rslt; } return 0; } =head2 first_element This method returns the first element in the node. =cut sub first_element { my ( $self ) = @_; return $self->{children}[0]; } =head2 first_token This method returns the first token in the node. If there is none, it returns nothing. =cut sub first_token { my ( $self ) = @_; my $elem = $self->first_element() or return; my $token; while ( ! ( $token = $elem->first_token() ) ) { $elem = $elem->next_element() or return; } return $token; } =head2 last_element This method returns the last element in the node. =cut sub last_element { my ( $self ) = @_; return $self->{children}[-1]; } =head2 last_token This method returns the last token in the node. If there is none, it returns nothing. =cut sub last_token { my ( $self ) = @_; my $elem = $self->last_element() or return; my $token; while ( ! ( $token = $elem->last_token() ) ) { $elem = $elem->previous_element() or return; } return $token; } sub location { my ( $self ) = @_; my $token = $self->first_token() or return undef; ## no critic (ProhibitExplicitReturnUndef) return $token->location(); } =head2 is_matcher This method returns a true value if any of the node's children does. Otherwise it returns C if any of the node's children does. Otherwise it returns a false (but defined) value. =cut sub is_matcher { my ( $self ) = @_; my $rslt = 0; foreach my $kid ( @{ $self->{children} } ) { my $kid_rslt = $kid->is_matcher() and return 1; defined $kid_rslt or $rslt = $kid_rslt; } return $rslt; } =head2 perl_version_introduced This method returns the maximum value of C returned by any of its elements. In other words, it returns the minimum version of Perl under which this node is valid. If there are no elements, 5.000 is returned, since that is the minimum value of Perl supported by this package. =cut sub perl_version_introduced { my ( $self ) = @_; return max( grep { defined $_ } MINIMUM_PERL, $self->{perl_version_introduced}, map { $_->perl_version_introduced() } $self->elements() ); } =head2 perl_version_removed This method returns the minimum defined value of C returned by any of the node's elements. In other words, it returns the lowest version of Perl in which this node is C valid. If there are no elements, or if no element has a defined C, C is returned. =cut sub perl_version_removed { my ( $self ) = @_; my $max; foreach my $elem ( $self->elements() ) { if ( defined ( my $ver = $elem->perl_version_removed() ) ) { if ( defined $max ) { $ver < $max and $max = $ver; } else { $max = $ver; } } } return $max; } sub remove_insignificant { my ( $self ) = @_; return $self->__new( map { $_->remove_insignificant() } $self->children() ); } =head2 schild This method returns the significant child at the given index; that is, C<< $node->schild(0) >> returns the first significant child, C<< $node->schild(1) >> returns the second significant child, and so on. Negative indices count from the end. =cut sub schild { my ( $self, $inx ) = @_; defined $inx or $inx = 0; my $kids = $self->{children}; if ( $inx >= 0 ) { my $loc = 0; while ( exists $kids->[$loc] ) { $kids->[$loc]->significant() or next; --$inx >= 0 and next; return $kids->[$loc]; } continue { $loc++; } } else { my $loc = -1; while ( exists $kids->[$loc] ) { $kids->[$loc]->significant() or next; $inx++ < -1 and next; return $kids->[$loc]; } continue { --$loc; } } return; } =head2 schildren This method returns the significant children of the Node. If called in scalar context it returns the number of significant children. =cut sub schildren { my ( $self ) = @_; if ( wantarray ) { return ( grep { $_->significant() } @{ $self->{children} } ); } elsif ( defined wantarray ) { my $kids = 0; foreach ( @{ $self->{children} } ) { $_->significant() and $kids++; } return $kids; } else { return; } } sub scontent { my ( $self ) = @_; # As of the invention of this method all nodes are significant, so # the following statement is pure paranoia on my part. -- TRW $self->significant() or return; # This needs to be elements(), not children() or schildren() -- or # selements() if that is ever invented. Not children() or # schildren() because those ignore the delimiters. Not selements() # (if that ever comes to pass) because scontent() has to make the # significance check, so selements() would be wasted effort. return join( '', map{ $_->scontent() } $self->elements() ); } sub tokens { my ( $self ) = @_; return ( map { $_->tokens() } $self->elements() ); } sub unescaped_content { my ( $self ) = @_; return join '', map { $_->unescaped_content() } $self->elements(); } # Help for nav(); sub __nav { my ( $self, $child ) = @_; refaddr( $child->parent() ) == refaddr( $self ) or return; my ( $method, $inx ) = $child->__my_nav() or return; return ( $method => [ $inx ] ); } sub __error { my ( $self, $msg, %arg ) = @_; defined $msg or $msg = 'Was class ' . ref $self; $self->{error} = $msg; bless $self, $self->ELEMENT_UNKNOWN(); foreach my $key ( keys %arg ) { $self->{$key} = $arg{$key}; } return 1; } # Called by the lexer once it has done its worst to all the tokens. # Called as a method with the lexer as argument. The return is the # number of parse failures discovered when finalizing. sub __PPIX_LEXER__finalize { my ( $self, $lexer ) = @_; my $rslt = 0; foreach my $elem ( $self->elements() ) { $rslt += $elem->__PPIX_LEXER__finalize( $lexer ); } return $rslt; } # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( $self, $number ) = @_; foreach my $kid ( $self->children() ) { $number = $kid->__PPIX_LEXER__record_capture_number( $number ); } return $number; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure.pm000444000765000024 2713414151156043 20220 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure - Represent a structure. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(foo)}' )->print(); =head1 INHERITANCE C is a L. C is the parent of L, L, L, L, L, L, L, L, L, L, L and L. =head1 DESCRIPTION This class represents a bracketed construction of some sort. The brackets are considered part of the structure, but not inside it. So the C method returns the brackets if they are defined, but the C method does not. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Structure; use strict; use warnings; use base qw{ PPIx::Regexp::Node }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ ARRAY_REF HASH_REF STRUCTURE_UNKNOWN @CARP_NOT }; use PPIx::Regexp::Util qw{ __instance }; use Scalar::Util qw{ refaddr }; our $VERSION = '0.082'; use constant ELEMENT_UNKNOWN => STRUCTURE_UNKNOWN; sub __new { my ( $class, @args ) = @_; my %brkt; if ( HASH_REF eq ref $args[0] ) { %brkt = %{ shift @args }; foreach my $key ( qw{ start type finish } ) { ARRAY_REF eq ref $brkt{$key} or $brkt{$key} = [ $brkt{$key} ]; } } else { $brkt{finish} = [ @args ? pop @args : () ]; $brkt{start} = [ @args ? shift @args : () ]; while ( @args && ! $args[0]->significant() ) { push @{ $brkt{start} }, shift @args; } $brkt{type} = []; if ( __instance( $args[0], 'PPIx::Regexp::Token::GroupType' ) ) { push @{ $brkt{type} }, shift @args; while ( @args && ! $args[0]->significant() ) { push @{ $brkt{type} }, shift @args; } } } $class->_check_for_interpolated_match( \%brkt, \@args ); my $self = $class->SUPER::__new( @args ) or return; if ( __instance( $brkt{type}[0], 'PPIx::Regexp::Token::GroupType' ) ) { ( my $reclass = ref $brkt{type}[0] ) =~ s/ Token::GroupType /Structure/smx; $reclass->can( 'start' ) or confess "Programming error - $reclass not loaded"; bless $self, $reclass; } foreach my $key ( qw{ start type finish } ) { $self->{$key} = []; ARRAY_REF eq ref $brkt{$key} or confess "Programming error - '$brkt{$key}' not an ARRAY"; foreach my $val ( @{ $brkt{$key} } ) { defined $val or next; __instance( $val, 'PPIx::Regexp::Element' ) or confess "Programming error - '$val' not a ", "PPIx::Regexp::Element"; push @{ $self->{$key} }, $val; $val->_parent( $self ); } } @{ $self->{finish} } or $self->{error} = 'Missing end delimiter'; return $self; } =head2 elements This override returns all components of the structure, including those that define it. =cut sub elements { my ( $self ) = @_; if ( wantarray ) { return ( @{ $self->{start} }, @{ $self->{type} }, @{ $self->{children} }, @{ $self->{finish} }, ); } elsif ( defined wantarray ) { my $size = scalar @{ $self->{start} }; $size += scalar @{ $self->{type} }; $size += scalar @{ $self->{children} }; $size += scalar @{ $self->{finish} }; return $size; } else { return; } } { my %explanation = ( q<(> => 'Grouping', # ) ); sub explain { my ( $self ) = @_; if ( my $type = $self->type() ) { return $type->explain(); } if ( my $start = $self->start() ) { # The check for a left parenthesis before returning # 'Grouping' is probably superflous, since it appears that # this method is overridden in all other cases where we # might get here (i.e. '[...]', '{...}'). But I'm paranoid. return $explanation{ $start->content() } || $start->explain(); } return $self->__no_explanation(); } } =head2 finish my $elem = $struct->finish(); my @elem = $struct->finish(); my $elem = $struct->finish( 0 ); Returns the finishing structure element. This is included in the C but not in the C. The finishing element is actually an array, though it should never have more than one element. Calling C in list context gets you all elements of the array. Calling it in scalar context gets you an element of the array, defaulting to element 0 if no argument is passed. =cut sub finish { my ( $self, $inx ) = @_; wantarray and return @{ $self->{finish} }; return $self->{finish}[ defined $inx ? $inx : 0 ]; } sub first_element { my ( $self ) = @_; $self->{start}[0] and return $self->{start}[0]; $self->{type}[0] and return $self->{type}[0]; if ( my $elem = $self->SUPER::first_element() ) { return $elem; } $self->{finish}[0] and return $self->{finish}[0]; return; } sub last_element { my ( $self ) = @_; $self->{finish}[-1] and return $self->{finish}[-1]; if ( my $elem = $self->SUPER::last_element() ) { return $elem; } $self->{type}[-1] and return $self->{type}[-1]; $self->{start}[-1] and return $self->{start}[-1]; return; } sub remove_insignificant { my ( $self ) = @_; return $self->__new( map { $_->remove_insignificant() } $self->elements() ); } =head2 start my $elem = $struct->start(); my @elem = $struct->start(); my $elem = $struct->start( 0 ); Returns the starting structure element. This is included in the C but not in the C. The starting element is actually an array. The first element (element 0) is the actual starting delimiter. Subsequent elements, if any, are insignificant elements (comments or white space) absorbed into the start element for ease of parsing subsequent elements. Calling C in list context gets you all elements of the array. Calling it in scalar context gets you an element of the array, defaulting to element 0 if no argument is passed. =cut sub start { my ( $self, $inx ) = @_; wantarray and return @{ $self->{start} }; return $self->{start}[ defined $inx ? $inx : 0 ]; } =head2 type my $elem = $struct->type(); my @elem = $struct->type(); my $elem = $struct->type( 0 ); Returns the group type if any. This will be the leading L token if any. This is included in C but not in C. The type is actually an array. The first element (element 0) is the actual type determiner. Subsequent elements, if any, are insignificant elements (comments or white space) absorbed into the type element for consistency with the way the start element is handled. Calling C in list context gets you all elements of the array. Calling it in scalar context gets you an element of the array, defaulting to element 0 if no argument is passed. =cut sub type { my ( $self, $inx ) = @_; wantarray and return @{ $self->{type} }; return $self->{type}[ defined $inx ? $inx : 0 ]; } # Check for things like (?$foo:...) or (?$foo) sub _check_for_interpolated_match { my ( undef, $brkt, $args ) = @_; # Invocant unused # Everything we are interested in begins with a literal '?' followed # by an interpolation. __instance( $args->[0], 'PPIx::Regexp::Token::Unknown' ) and $args->[0]->content() eq '?' and __instance( $args->[1], 'PPIx::Regexp::Token::Interpolation' ) or return; my $hiwater = 2; # Record how far we got into the arguments for # subsequent use detecting things like # (?$foo). # If we have a literal ':' as the third argument: # GroupType::Modifier, rebless the ':' so we know not to match # against it, and splice all three tokens into the type. if ( __instance( $args->[2], 'PPIx::Regexp::Token::Literal' ) && $args->[2]->content() eq ':' ) { # Rebless the '?' as a GroupType::Modifier. PPIx::Regexp::Token::GroupType::Modifier->__PPIX_ELEM__rebless( $args->[0] ); # Rebless the ':' as a GroupType, just so it does not look like # something to match against. PPIx::Regexp::Token::GroupType->__PPIX_ELEM__rebless( $args->[2] ); # Shove our three significant tokens into the type. push @{ $brkt->{type} }, splice @{ $args }, 0, 3; # Stuff all the immediately-following insignificant tokens into # the type as well. while ( @{ $args } && ! $args->[0]->significant() ) { push @{ $brkt->{type} }, shift @{ $args }; } # Return to the caller, since we have done all the damage we # can. return; } # If we have a literal '-' as the third argument, we might have # something like (?$on-$off:$foo). if ( __instance( $args->[2], 'PPIx::Regexp::Token::Literal' ) && $args->[2]->content() eq '-' && __instance( $args->[3], 'PPIx::Regexp::Token::Interpolation' ) ) { $hiwater = 4; if ( __instance( $args->[4], 'PPIx::Regexp::Token::Literal' ) && $args->[4]->content() eq ':' ) { # Rebless the '?' as a GroupType::Modifier. PPIx::Regexp::Token::GroupType::Modifier->__PPIX_ELEM__rebless( $args->[0] ); # Rebless the '-' and ':' as GroupType, just so they do not # look like something to match against. PPIx::Regexp::Token::GroupType->__PPIX_ELEM__rebless( $args->[2] ); PPIx::Regexp::Token::GroupType->__PPIX_ELEM__rebless( $args->[4] ); # Shove our five significant tokens into the type. push @{ $brkt->{type} }, splice @{ $args }, 0, 5; # Stuff all the immediately-following insignificant tokens # into the type as well. while ( @{ $args } && ! $args->[0]->significant() ) { push @{ $brkt->{type} }, shift @{ $args }; } # Return to the caller, since we have done all the damage we # can. return; } } # If the group contains _any_ significant tokens at this point, we # do _not_ have something like (?$foo). foreach my $inx ( $hiwater .. $#$args ) { $args->[$inx]->significant() and return; } # Rebless the '?' as a GroupType::Modifier. PPIx::Regexp::Token::GroupType::Modifier->__PPIX_ELEM__rebless( $args->[0] ); # Shove all the contents of $args into type, using splice to leave # @{ $args } empty after we do this. push @{ $brkt->{type} }, splice @{ $args }; # We have done all the damage we can. return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Support.pm000444000765000024 727314151156043 17656 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Support - Basis for the PPIx::Regexp support classes =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is not descended from any other class. C is the parent of L, L and L. =head1 DESCRIPTION This abstract class provides methods for the C support classes. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Support; use strict; use warnings; use PPIx::Regexp::Constant qw{ @CARP_NOT }; use PPIx::Regexp::Util qw{ __instance }; our $VERSION = '0.082'; =head2 close_bracket This method takes as its argument a character. If this character is an open bracket the corresponding close bracket is returned. Otherwise C is returned. Only the ASCII bracket characters are considered brackets: (), {}, [], and <>. =cut { my %bracket = ( '(' => ')', '{' => '}', '<' => '>', '[' => ']', ); sub close_bracket { my ( undef, $char ) = @_; # Invocant unused defined $char or return; __instance( $char, 'PPIx::Regexp::Element' ) and $char = $char->content(); return $bracket{$char}; } } =head2 decode This method wraps the Encode::decode subroutine. If the object specifies no encoding or encode_available() returns false, this method simply returns its input string. =cut sub decode { my ( $self, $data ) = @_; defined $self->{encoding} or return $data; encode_available() or return $data; return Encode::decode( $self->{encoding}, $data ); } =head2 encode This method wraps the Encode::encode subroutine. If the object specifies no encoding or encode_available() returns false, this method simply returns its input string. =cut sub encode { my ( $self, $data ) = @_; defined $self->{encoding} or return $data; encode_available() or return $data; return Encode::encode( $self->{encoding}, $data ); } =head2 encode_available This method returns true if the Encode module is available, and false otherwise. If it returns true, the Encode module has actually been loaded. =cut { my $encode_available; sub encode_available { defined $encode_available and return $encode_available; return ( $encode_available = eval { require Encode; 1; } ? 1 : 0 ); } } # This method is to be used only by the PPIx-Regexp package. It returns # the first of its arguments which is defined. It will go away when # (or if!) these modules get 'use 5.010;' at the top. sub __defined_or { my ( undef, @args ) = @_; # Invocant unused foreach my $arg ( @args ) { defined $arg and return $arg; } return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token.pm000444000765000024 1010114151156043 17262 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token - Base class for PPIx::Regexp tokens. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}' )->print(); =head1 INHERITANCE C is a L. C is the parent of L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L and L. =head1 DESCRIPTION This class represents the base of the class hierarchy for tokens in the L package. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token; use strict; use warnings; use base qw{PPIx::Regexp::Element}; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; use constant TOKENIZER_ARGUMENT_REQUIRED => 0; sub __new { my ( $class, $content, %arg ) = @_; not $class->TOKENIZER_ARGUMENT_REQUIRED() or $arg{tokenizer} or confess 'Programming error - tokenizer not provided'; my $self = { content => $content, }; foreach my $key ( qw{ explanation perl_version_introduced perl_version_removed } ) { defined $arg{$key} and $self->{$key} = $arg{$key}; } bless $self, ref $class || $class; return $self; } sub content { my ( $self ) = @_; return $self->{content}; } =head2 first_token This method returns its invocant. =cut sub first_token { my ( $self ) = @_; return $self; } =head2 last_token This method returns its invocant. =cut sub last_token { my ( $self ) = @_; return $self; } sub perl_version_introduced { my ( $self ) = @_; return defined $self->{perl_version_introduced} ? $self->{perl_version_introduced} : MINIMUM_PERL; } sub perl_version_removed { my ( $self ) = @_; return $self->{perl_version_removed}; } sub unescaped_content { my ( $self ) = @_; my $content = $self->content(); $content =~ s/ \\ (?= . ) //smxg; return $content; } sub scontent { my ( $self ) = @_; $self->significant() and return $self->{content}; return; } # Called by the lexer once it has done its worst to all the tokens. # Called as a method with the lexer as argument. The return is the # number of parse failures discovered when finalizing. sub __PPIX_LEXER__finalize { return 0; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Tokenizer.pm000444000765000024 14475014151156043 20216 0ustar00tomstaff000000000000package PPIx::Regexp::Tokenizer; use strict; use warnings; use base qw{ PPIx::Regexp::Support }; use Carp qw{ carp croak confess }; use PPIx::Regexp::Constant qw{ ARRAY_REF CODE_REF HASH_REF LOCATION_LINE LOCATION_CHARACTER LOCATION_COLUMN LOCATION_LOGICAL_LINE MINIMUM_PERL REGEXP_REF TOKEN_LITERAL TOKEN_UNKNOWN @CARP_NOT }; use PPIx::Regexp::Token::Assertion (); use PPIx::Regexp::Token::Backreference (); use PPIx::Regexp::Token::Backtrack (); use PPIx::Regexp::Token::CharClass::POSIX (); use PPIx::Regexp::Token::CharClass::POSIX::Unknown (); use PPIx::Regexp::Token::CharClass::Simple (); use PPIx::Regexp::Token::Code (); use PPIx::Regexp::Token::Comment (); use PPIx::Regexp::Token::Condition (); use PPIx::Regexp::Token::Control (); use PPIx::Regexp::Token::Delimiter (); use PPIx::Regexp::Token::Greediness (); use PPIx::Regexp::Token::GroupType::Assertion (); use PPIx::Regexp::Token::GroupType::Atomic_Script_Run (); use PPIx::Regexp::Token::GroupType::BranchReset (); use PPIx::Regexp::Token::GroupType::Code (); use PPIx::Regexp::Token::GroupType::Modifier (); use PPIx::Regexp::Token::GroupType::NamedCapture (); use PPIx::Regexp::Token::GroupType::Script_Run (); use PPIx::Regexp::Token::GroupType::Subexpression (); use PPIx::Regexp::Token::GroupType::Switch (); use PPIx::Regexp::Token::Interpolation (); use PPIx::Regexp::Token::Literal (); use PPIx::Regexp::Token::Modifier (); use PPIx::Regexp::Token::Operator (); use PPIx::Regexp::Token::Quantifier (); use PPIx::Regexp::Token::Recursion (); use PPIx::Regexp::Token::Structure (); use PPIx::Regexp::Token::Unknown (); use PPIx::Regexp::Token::Whitespace (); use PPIx::Regexp::Util qw{ is_ppi_regexp_element __instance }; use Scalar::Util qw{ looks_like_number }; our $VERSION = '0.082'; our $DEFAULT_POSTDEREF; defined $DEFAULT_POSTDEREF or $DEFAULT_POSTDEREF = 1; { # Names of classes containing tokenization machinery. There are few # known ordering requirements, since each class recognizes its own, # and I have tried to prevent overlap. Absent such constraints, the # order is in perceived frequency of acceptance, to keep the search # as short as possible. If I were conscientious I would gather # statistics on this. my @classes = ( # TODO make readonly when acceptable way appears 'PPIx::Regexp::Token::Literal', 'PPIx::Regexp::Token::Interpolation', 'PPIx::Regexp::Token::Control', # Note 1 'PPIx::Regexp::Token::CharClass::Simple', # Note 2 'PPIx::Regexp::Token::Quantifier', 'PPIx::Regexp::Token::Greediness', 'PPIx::Regexp::Token::CharClass::POSIX', # Note 3 'PPIx::Regexp::Token::Structure', 'PPIx::Regexp::Token::Assertion', 'PPIx::Regexp::Token::Backreference', 'PPIx::Regexp::Token::Operator', # Note 4 ); # Note 1: If we are in quote mode ( \Q ... \E ), Control makes a # literal out of anything it sees other than \E. So it # needs to come before almost all other tokenizers. Not # Literal, which already makes literals, and not # Interpolation, which is legal in quote mode, but # everything else. # Note 2: CharClass::Simple must come after Literal, because it # relies on Literal to recognize a Unicode named character # ( \N{something} ), so any \N that comes through to it # must be the \N simple character class (which represents # anything but a newline, and was introduced in Perl # 5.11.0. # Note 3: CharClass::POSIX has to come before Structure, since both # look for square brackets, and CharClass::POSIX is the # more particular. # Note 4: Operator relies on Literal making the characters literal # if they appear in a context where they can not be # operators, and Control making them literals if quoting, # so it must come after both. # Return the declared tokenizer classes. sub __tokenizer_classes { return @classes; } } { my $errstr; sub new { my ( $class, $re, %args ) = @_; ref $class and $class = ref $class; $errstr = undef; exists $args{default_modifiers} and ARRAY_REF ne ref $args{default_modifiers} and do { $errstr = 'default_modifiers must be an array reference'; return; }; defined $args{postderef} and __PACKAGE__->_deprecation_notice( attribute => 'postderef' ); my $self = { index_locations => $args{index_locations}, # Index locations capture => undef, # Captures from find_regexp. content => undef, # The string we are tokenizing. cookie => {}, # Cookies cursor_curr => 0, # The current position in the string. cursor_limit => undef, # The end of the portion of the # string being tokenized. cursor_orig => undef, # Position of cursor when tokenizer # called. Used by get_token to prevent # recursion. cursor_modifiers => undef, # Position of modifiers. default_modifiers => $args{default_modifiers} || [], delimiter_finish => undef, # Finishing delimiter of regexp. delimiter_start => undef, # Starting delimiter of regexp. encoding => $args{encoding}, # Character encoding. expect => undef, # Extra classes to expect. expect_next => undef, # Extra classes as of next parse cycle failures => 0, # Number of parse failures. find => undef, # String for find_regexp known => {}, # Known tokenizers, by mode. location => $args{location}, match => undef, # Match from find_regexp. mode => 'init', # Initialize modifiers => [{}], # Modifier hash. pending => [], # Tokens made but not returned. postderef => defined $args{postderef} ? $args{postderef} : 1, prior => TOKEN_UNKNOWN, # Prior significant token. source => $re, # The object we were initialized with. strict => $args{strict}, # like "use re 'strict';". trace => __PACKAGE__->__defined_or( $args{trace}, $ENV{PPIX_REGEXP_TOKENIZER_TRACE}, 0 ), }; if ( __instance( $re, 'PPI::Element' ) ) { is_ppi_regexp_element( $re ) or return __set_errstr( ref $re, 'not supported by', $class ); # TODO conditionalizstion on PPI class does not really # belong here, but at the moment I have no other idea of # where to put it. $self->{content} = $re->isa( 'PPI::Token::HereDoc' ) ? join( '', $re->content(), "\n", $re->heredoc(), $re->terminator(), "\n" ) : $re->content(); } elsif ( ref $re ) { return __set_errstr( ref $re, 'not supported' ); } else { $self->{content} = $re; } bless $self, $class; $self->{content} = $self->decode( $self->{content} ); $self->{cursor_limit} = length $self->{content}; $self->{trace} and warn "\ntokenizing '$self->{content}'\n"; return $self; } sub __set_errstr { $errstr = join ' ', @_; return; } sub errstr { return $errstr; } } sub capture { my ( $self ) = @_; $self->{capture} or return; defined wantarray or return; return wantarray ? @{ $self->{capture} } : $self->{capture}; } sub content { my ( $self ) = @_; return $self->{content}; } sub cookie { my ( $self, $name, @args ) = @_; defined $name or confess "Programming error - undefined cookie name"; if ( $self->{trace} ) { local $" = ', '; warn "cookie( '$name', @args )\n"; } @args or return $self->{cookie}{$name}; my $cookie = shift @args; if ( CODE_REF eq ref $cookie ) { return ( $self->{cookie}{$name} = $cookie ); } elsif ( defined $cookie ) { confess "Programming error - cookie must be CODE ref or undef"; } else { return delete $self->{cookie}{$name}; } } # NOTE: Currently this is called only against # COOKIE_LOOKAROUND_ASSERTION, once in PPIx::Token::GroupType::Assertion # to prevent the cookie from being remade if it already exists, and once # in PPIx::Regexp::Token::Assertion to determine if \K is inside a # lookaround assertion. If it gets used other places, or if there is # call for it, I should consider removing the underscores and # documenting it as public. sub __cookie_exists { my ( $self, $name ) = @_; defined $name or confess "Programming error - undefined cookie name"; return $self->{cookie}{$name}; } sub default_modifiers { my ( $self ) = @_; return [ @{ $self->{default_modifiers} } ]; } sub __effective_modifiers { my ( $self ) = @_; HASH_REF eq ref $self->{effective_modifiers} or return {}; return { %{ $self->{effective_modifiers} } }; } sub encoding { my ( $self ) = @_; return $self->{encoding}; } sub expect { my ( $self, @args ) = @_; @args or return; $self->{expect_next} = [ map { m/ \A PPIx::Regexp:: /smx ? $_ : 'PPIx::Regexp::' . $_ } @args ]; $self->{expect} = undef; return; } sub failures { my ( $self ) = @_; return $self->{failures}; } sub find_matching_delimiter { my ( $self ) = @_; $self->{cursor_curr} ||= 0; my $start = substr $self->{content}, $self->{cursor_curr}, 1; my $inx = $self->{cursor_curr}; my $finish = ( my $bracketed = $self->close_bracket( $start ) ) || $start; =begin comment $self->{trace} and warn "Find matching delimiter: Start with '$start' at $self->{cursor_curr}, end with '$finish' at or before $self->{cursor_limit}\n"; =end comment =cut my $nest = 0; while ( ++$inx < $self->{cursor_limit} ) { my $char = substr $self->{content}, $inx, 1; =begin comment $self->{trace} and warn " looking at '$char' at $inx, nest level $nest\n"; =end comment =cut if ( $char eq '\\' && $finish ne '\\' ) { ++$inx; } elsif ( $bracketed && $char eq $start ) { ++$nest; } elsif ( $char eq $finish ) { --$nest < 0 and return $inx - $self->{cursor_curr}; } } return; } sub find_regexp { my ( $self, $regexp ) = @_; REGEXP_REF eq ref $regexp or confess 'Argument is a ', ( ref $regexp || 'scalar' ), ' not a Regexp'; defined $self->{find} or $self->_remainder(); $self->{find} =~ $regexp or return; my @capture; foreach my $inx ( 0 .. $#+ ) { if ( defined $-[$inx] && defined $+[$inx] ) { push @capture, $self->{capture} = substr $self->{find}, $-[$inx], $+[$inx] - $-[$inx]; } else { push @capture, undef; } } $self->{match} = shift @capture; $self->{capture} = \@capture; # The following circumlocution seems to be needed under Perl 5.13.0 # for reasons I do not fathom -- at least in the case where # wantarray is false. RT 56864 details the symptoms, which I was # never able to reproduce outside Perl::Critic. But returning $+[0] # directly, the value could transmogrify between here and the # calling module. ## my @data = ( $-[0], $+[0] ); ## return wantarray ? @data : $data[1]; return wantarray ? ( $-[0] + 0, $+[0] + 0 ) : $+[0] + 0; } sub get_mode { my ( $self ) = @_; return $self->{mode}; } sub get_start_delimiter { my ( $self ) = @_; return $self->{delimiter_start}; } sub get_token { my ( $self ) = @_; caller eq __PACKAGE__ or $self->{cursor_curr} > $self->{cursor_orig} or confess 'Programming error - get_token() called without ', 'first calling make_token()'; my $handler = '__PPIX_TOKENIZER__' . $self->{mode}; my $code = $self->can( $handler ) or confess 'Programming error - ', "Getting token in mode '$self->{mode}'. ", "cursor_curr = $self->{cursor_curr}; ", "cursor_limit = $self->{cursor_limit}; ", "length( content ) = ", length $self->{content}, "; content = '$self->{content}'"; my $character = substr( $self->{content}, $self->{cursor_curr}, 1 ); $self->{trace} and warn "get_token() got '$character' from $self->{cursor_curr}\n"; return ( $code->( $self, $character ) ); } sub interpolates { my ( $self ) = @_; return $self->{delimiter_start} ne q{'}; } sub make_token { my ( $self, $length, $class, $arg ) = @_; defined $class or $class = caller; if ( $length + $self->{cursor_curr} > $self->{cursor_limit} ) { $length = $self->{cursor_limit} - $self->{cursor_curr} or return; } $class =~ m/ \A PPIx::Regexp:: /smx or $class = 'PPIx::Regexp::' . $class; my $content = substr $self->{content}, $self->{cursor_curr}, $length; $self->{trace} and warn "make_token( $length, '$class' ) => '$content'\n"; $self->{trace} > 1 and warn " make_token: cursor_curr = $self->{cursor_curr}; ", "cursor_limit = $self->{cursor_limit}\n"; my $token = $class->__new( $content, tokenizer => $self, %{ $arg || {} } ) or return; $self->{index_locations} and $self->_update_location( $token ); $token->significant() and $self->{expect} = undef; $token->isa( TOKEN_UNKNOWN ) and $self->{failures}++; $self->{cursor_curr} += $length; $self->{find} = undef; $self->{match} = undef; $self->{capture} = undef; foreach my $name ( keys %{ $self->{cookie} } ) { my $cookie = $self->{cookie}{$name}; $cookie->( $self, $token ) or delete $self->{cookie}{$name}; } # Record this token as the prior token if it is significant. We must # do this after processing cookies, so that the cookies have access # to the old token if they want. $token->significant() and $self->{prior_significant_token} = $token; return $token; } sub match { my ( $self ) = @_; return $self->{match}; } sub modifier { my ( $self, $modifier ) = @_; return PPIx::Regexp::Token::Modifier::__asserts( $self->{modifiers}[-1], $modifier ); } sub modifier_duplicate { my ( $self ) = @_; push @{ $self->{modifiers} }, { %{ $self->{modifiers}[-1] } }; return; } sub modifier_modify { my ( $self, %args ) = @_; # Modifier code is centralized in PPIx::Regexp::Token::Modifier $self->{modifiers}[-1] = PPIx::Regexp::Token::Modifier::__PPIX_TOKENIZER__modifier_modify( $self->{modifiers}[-1], \%args ); return; } sub modifier_pop { my ( $self ) = @_; @{ $self->{modifiers} } > 1 and pop @{ $self->{modifiers} }; return; } sub modifier_seen { my ( $self, $modifier ) = @_; foreach my $mod ( reverse @{ $self->{modifiers} } ) { exists $mod->{$modifier} and return 1; } return; } sub next_token { my ( $self ) = @_; { if ( @{ $self->{pending} } ) { return shift @{ $self->{pending} }; } if ( $self->{cursor_curr} >= $self->{cursor_limit} ) { $self->{cursor_limit} >= length $self->{content} and return; $self->{mode} eq 'finish' and return; $self->_set_mode( 'finish' ); $self->{cursor_limit} += length $self->{delimiter_finish}; } if ( my @tokens = $self->get_token() ) { push @{ $self->{pending} }, @tokens; redo; } } return; } sub peek { my ( $self, $offset ) = @_; defined $offset or $offset = 0; $offset < 0 and return; $offset += $self->{cursor_curr}; $offset >= $self->{cursor_limit} and return; return substr $self->{content}, $offset, 1; } sub ppi_document { my ( $self ) = @_; defined $self->{find} or $self->_remainder(); return PPI::Document->new( \"$self->{find}" ); } sub prior_significant_token { my ( $self, $method, @args ) = @_; defined $method or return $self->{prior_significant_token}; $self->{prior_significant_token}->can( $method ) or confess 'Programming error - ', ( ref $self->{prior_significant_token} || $self->{prior_significant_token} ), ' does not support method ', $method; return $self->{prior_significant_token}->$method( @args ); } # my $length = $token->__recognize_postderef( $tokenizer, $iterator ). # # This method is private to the PPIx-Regexp package, and may be changed # or retracted without warning. What it does is to recognize postfix # dereferences. It returns the length in characters of the first postfix # dereference found, or a false value if none is found. This returns # false immediately unless the tokenizer was instantiated with the # C argument true, or if it was not specified and # C<$DEFAULT_POSTDEREF> was true when the tokenizer was instantiated. # # The optional $iterator argument can be one of the following: # - A code reference, which will be called to provide PPI::Element # objects to be checked to see if they represent a postfix # dereference. # - A PPI::Element, which is checked to see if it is a postfix # dereference. # - Undef, or omitted, in which case ppi() is called on the invocant, # and everything that follows the '->' operator is checked to see if # it is a postfix dereference. # - Anything else results in an exception and stack trace. { # %* &* ** my %magic_var = map { $_ => 1 } qw{ @* $* }; my %magic_oper = map { $_ => 1 } qw{ & ** % }; my %sliceable = map { $_ => 1 } qw{ @ % }; my %post_slice = map { $_ => 1 } qw< { [ >; # ] } sub __recognize_postderef { my ( $self, $token, $iterator ) = @_; $self->{postderef} or return; # Note that if ppi() gets called I have to hold a reference to # the returned object until I am done with all its children. my $ppi; if ( ! defined $iterator ) { # This MUST be done before ppi() is called. $self->{index_locations} and $self->_update_location( $token ); $ppi = $token->ppi(); my @ops = grep { '->' eq $_->content() } @{ $ppi->find( 'PPI::Token::Operator' ) || [] }; $iterator = sub { my $op = shift @ops or return; return $op->snext_sibling(); }; } elsif ( $iterator->isa( 'PPI::Element' ) ) { my @eles = ( $iterator ); $iterator = sub { return shift @eles; }; } elsif ( CODE_REF ne ref $iterator ) { confess 'Programming error - Iterator not understood'; } my $accept = $token->__postderef_accept_cast(); while ( my $elem = $iterator->() ) { my $content = $elem->content(); $content =~ m/ \A ( . \#? ) /smx and $accept->{$1} or next; my $length = length $content; # PPI parses '$x->@*' as containing magic variable '@*'. # Similarly for '$*' and '$#*'. I think this is a bug, and # they should be parsed as casts, but ... if ( $elem->isa( 'PPI::Token::Magic' ) ) { $magic_var{$content} and return $length; if ( '$#' eq $content ) { my $next = $elem->snext_sibling() or return $length; '*' eq substr $next->content(), 0, 1 and return $length + 1; } } # PPI parses '%*' as a cast of '%' followed by a splat, but # I think it is likely that if it ever supports postderef # operators that they will be casts. It currently parses # '**' as an operator and '&*' as two operators, but the # logic is pretty much the same as for a cast, so they get # handled here too. if ( $elem->isa( 'PPI::Token::Cast' ) || $elem->isa( 'PPI::Token::Operator' ) && $magic_oper{$content} ) { # Maybe PPI will eventually parse something like '$*' as # a cast, so ... $content =~ m/ [*] \z /smx and return $length; # Or maybe it will parse the asterisk separately, but I # have no idea what its class will be. my $next = $elem->snext_sibling() or return; my $next_content = $next->content(); my $next_char = substr $next_content, 0, 1; '*' eq $next_char and return $length + 1; # We may still have a slice. $sliceable{$content} and $post_slice{$next_char} and return $length + length $next_content; # TODO maybe PPI will do something completely # unanticipated with postderef. } # Otherwise, we're not a postfix dereference; try the next # iteration. } # No postfix dereference found. return; } } sub significant { return 1; } sub strict { my ( $self ) = @_; return $self->{strict}; } sub _known_tokenizers { my ( $self ) = @_; my $mode = $self->{mode}; my @expect; if ( $self->{expect_next} ) { $self->{expect} = $self->{expect_next}; $self->{expect_next} = undef; } if ( $self->{expect} ) { @expect = $self->_known_tokenizer_check( @{ $self->{expect} } ); } exists $self->{known}{$mode} and return ( @expect, @{ $self->{known}{$mode} } ); my @found = $self->_known_tokenizer_check( $self->__tokenizer_classes() ); $self->{known}{$mode} = \@found; return (@expect, @found); } sub _known_tokenizer_check { my ( $self, @args ) = @_; my $handler = '__PPIX_TOKENIZER__' . $self->{mode}; my @found; foreach my $class ( @args ) { $class->can( $handler ) or next; push @found, $class; } return @found; } sub tokens { my ( $self ) = @_; my @rslt; while ( my $token = $self->next_token() ) { push @rslt, $token; } return @rslt; } # $self->_deprecation_notice( $type, $name ); # # This method centralizes deprecation. Type is 'attribute' or # 'method'. Deprecation is driven of the %deprecate hash. Values # are: # false - no warning # 1 - warn on first use # 2 - warn on each use # 3 - die on each use. # # $self->_deprecation_in_progress( $type, $name ) # # This method returns true if the deprecation is in progress. In # fact it returns the deprecation level. { my %deprecate = ( attribute => { postderef => 3, }, ); sub _deprecation_notice { my ( undef, $type, $name, $repl ) = @_; # Invocant unused $deprecate{$type} or return; $deprecate{$type}{$name} or return; my $msg = sprintf 'The %s %s is %s', $name, $type, $deprecate{$type}{$name} > 2 ? 'removed' : 'deprecated'; defined $repl and $msg .= "; use $repl instead"; $deprecate{$type}{$name} >= 3 and croak $msg; warnings::enabled( 'deprecated' ) and carp $msg; $deprecate{$type}{$name} == 1 and $deprecate{$type}{$name} = 0; return; } =begin comment sub _deprecation_in_progress { my ( $self, $type, $name ) = @_; $deprecate{$type} or return; return $deprecate{$type}{$name}; } =end comment =cut } sub _remainder { my ( $self ) = @_; $self->{cursor_curr} > $self->{cursor_limit} and confess "Programming error - Trying to find past end of string"; $self->{find} = substr( $self->{content}, $self->{cursor_curr}, $self->{cursor_limit} - $self->{cursor_curr} ); return; } sub _make_final_token { my ( $self, $len, $class, $arg ) = @_; my $token = $self->make_token( $len, $class, $arg ); $self->_set_mode( 'kaput' ); return $token; } sub _set_mode { my ( $self, $mode ) = @_; $self->{trace} and warn "Tokenizer going from mode $self->{mode} to $mode\n"; $self->{mode} = $mode; if ( 'kaput' eq $mode ) { $self->{cursor_curr} = $self->{cursor_limit} = length $self->{content}; } return; } sub __init_error { my ( $self , $err ) = @_; defined $err or $err = 'Tokenizer found illegal first characters'; return $self->_make_final_token( length $self->{content}, TOKEN_UNKNOWN, { error => $err, }, ); } sub _update_location { my ( $self, $token ) = @_; $token->{location} # Idempotent and return; my $loc = $self->{_location} ||= do { my %loc = ( line_content => '', location => $self->{location}, ); if ( __instance( $self->{source}, 'PPI::Element' ) ) { $loc{location} ||= $self->{source}->location(); if ( my $doc = $self->{source}->document() ) { $loc{tab_width} = $doc->tab_width(); } } $loc{tab_width} ||= 1; \%loc; }; $loc->{location} or return; $token->{location} = [ @{ $loc->{location} } ]; if ( defined( my $content = $token->content() ) ) { if ( my $newlines = $content =~ tr/\n/\n/ ) { $loc->{location}[LOCATION_LINE] += $newlines; $loc->{location}[LOCATION_LOGICAL_LINE] += $newlines; $content =~ s/ .* \n //smx; $loc->{location}[LOCATION_CHARACTER] = $loc->{location}[LOCATION_COLUMN] = 1; $loc->{line_content} = ''; } $loc->{location}[LOCATION_CHARACTER] += length $content; $loc->{line_content} .= $content; local $Text::Tabs::tabstop = $loc->{tab_width}; $loc->{location}[LOCATION_COLUMN] = 1 + length Text::Tabs::expand( $loc->{line_content} ); } return; } sub __PPIX_TOKENIZER__init { my ( $self ) = @_; $self->find_regexp( qr{ \A ( \s* ) ( qr | m | s )? ( \s* ) ( . ) }smx ) or return $self->__init_error(); my ( $leading_white, $type, $next_white, $delim_start ) = $self->capture(); defined $type or $type = ''; $type or $delim_start =~ m< \A [/?] \z >smx or return $self->__init_error(); $type and not $next_white and $delim_start =~ m< \A \w \z >smx and return $self->__init_error(); $self->{type} = $type; my @tokens; '' ne $leading_white and push @tokens, $self->make_token( length $leading_white, 'PPIx::Regexp::Token::Whitespace' ); push @tokens, $self->make_token( length $type, 'PPIx::Regexp::Token::Structure' ); '' ne $next_white and push @tokens, $self->make_token( length $next_white, 'PPIx::Regexp::Token::Whitespace' ); $self->{delimiter_start} = $delim_start; $self->{trace} and warn "Tokenizer found regexp start delimiter '$delim_start' at $self->{cursor_curr}\n"; if ( my $offset = $self->find_matching_delimiter() ) { my $cursor_limit = $self->{cursor_curr} + $offset; $self->{trace} and warn "Tokenizer found regexp end delimiter at $cursor_limit\n"; if ( $self->__number_of_extra_parts() ) { ### my $found_embedded_comments; if ( $self->close_bracket( $self->{delimiter_start} ) ) { pos $self->{content} = $self->{cursor_curr} + $offset + 1; # If we're bracketed, there may be Perl comments between # the regex and the replacement. PPI gets the parse # wrong as of 1.220, but if we get the handling of the # underlying string right, we will Just Work when PPI # gets it right. while ( $self->{content} =~ m/ \G \s* \n \s* \# [^\n]* /smxgc ) { ## $found_embedded_comments = 1; } $self->{content} =~ m/ \s* /smxgc; } else { pos $self->{content} = $self->{cursor_curr} + $offset; } # Localizing cursor_curr and delimiter_start would be # cleaner, but I don't want the old values restored if a # parse error occurs. my $cursor_curr = $self->{cursor_curr}; my $delimiter_start = $self->{delimiter_start}; $self->{cursor_curr} = pos $self->{content}; $self->{delimiter_start} = substr $self->{content}, $self->{cursor_curr}, 1; $self->{trace} and warn "Tokenizer found replacement start delimiter '$self->{delimiter_start}' at $self->{cursor_curr}\n"; if ( my $s_off = $self->find_matching_delimiter() ) { $self->{cursor_modifiers} = $self->{cursor_curr} + $s_off + 1; $self->{trace} and warn "Tokenizer found replacement end delimiter at @{[ $self->{cursor_curr} + $s_off ]}\n"; $self->{cursor_curr} = $cursor_curr; $self->{delimiter_start} = $delimiter_start; } else { $self->{trace} and warn 'Tokenizer failed to find replacement', "end delimiter starting at $self->{cursor_curr}\n"; $self->{cursor_curr} = 0; # TODO If I were smart enough here I could check for # PPI mis-parses like s{foo} # #{bar} # {baz} # here, doing so if $found_embedded_comments (commented # out above) is true. The problem is that there seem to # as many mis-parses as there are possible delimiters. return $self->__init_error( 'Tokenizer found mismatched replacement delimiters', ); } } else { $self->{cursor_modifiers} = $cursor_limit + 1; } $self->{cursor_limit} = $cursor_limit; } else { $self->{cursor_curr} = 0; return $self->_make_final_token( length( $self->{content} ), TOKEN_UNKNOWN, { error => 'Tokenizer found mismatched regexp delimiters', }, ); } { # We have to instantiate the trailing tokens now so we can # figure out what modifiers are in effect. But we can't # index their locations (if desired) because they are being # instantiated out of order local $self->{index_locations} = 0; my @mods = @{ $self->{default_modifiers} }; pos $self->{content} = $self->{cursor_modifiers}; local $self->{cursor_curr} = $self->{cursor_modifiers}; local $self->{cursor_limit} = length $self->{content}; my @trailing; { my $len = $self->find_regexp( qr{ \A [[:lower:]]* }smx ); push @trailing, $self->make_token( $len, 'PPIx::Regexp::Token::Modifier' ); } if ( my $len = $self->find_regexp( qr{ \A \s+ }smx ) ) { push @trailing, $self->make_token( $len, 'PPIx::Regexp::Token::Whitespace' ); } if ( my $len = $self->find_regexp( qr{ \A .+ }smx ) ) { push @trailing, $self->make_token( $len, TOKEN_UNKNOWN, { error => 'Trailing characters after expression', } ); } $self->{trailing_tokens} = \@trailing; push @mods, $trailing[0]->content(); $self->{effective_modifiers} = PPIx::Regexp::Token::Modifier::__aggregate_modifiers ( @mods ); $self->{modifiers} = [ { %{ $self->{effective_modifiers} } }, ]; } $self->{delimiter_finish} = substr $self->{content}, $self->{cursor_limit}, 1; push @tokens, $self->make_token( 1, 'PPIx::Regexp::Token::Delimiter' ); $self->_set_mode( 'regexp' ); $self->{find} = undef; return @tokens; } # Match the initial part of the regexp including any leading white # space. The initial delimiter is the first thing not consumed, though # we check it for sanity. sub __initial_match { my ( $self ) = @_; $self->find_regexp( qr{ \A ( \s* ) ( qr | m | s )? ( \s* ) (?: [^\w\s] ) }smx ) or return; my ( $leading_white, $type, $next_white ) = $self->capture(); defined $type or $type = ''; $self->{type} = $type; my @tokens; '' ne $leading_white and push @tokens, $self->make_token( length $leading_white, 'PPIx::Regexp::Token::Whitespace' ); push @tokens, $self->make_token( length $type, 'PPIx::Regexp::Token::Structure' ); '' ne $next_white and push @tokens, $self->make_token( length $next_white, 'PPIx::Regexp::Token::Whitespace' ); return @tokens; } { my %extra_parts = ( s => 1, ); # Return the number of extra delimited parts. This will be 0 except # for s///, which will be 1. sub __number_of_extra_parts { my ( $self ) = @_; return $extra_parts{$self->{type}} || 0; } } { my @part_class = qw{ PPIx::Regexp::Structure::Regexp PPIx::Regexp::Structure::Replacement }; # Return the classes for the parts of the expression. sub __part_classes { my ( $self ) = @_; my $max = $self->__number_of_extra_parts(); return @part_class[ 0 .. $max ]; } } sub __PPIX_TOKENIZER__regexp { my ( $self, $character ) = @_; my $mode = $self->{mode}; my $handler = '__PPIX_TOKENIZER__' . $mode; $self->{cursor_orig} = $self->{cursor_curr}; foreach my $class ( $self->_known_tokenizers() ) { my @tokens = grep { $_ } $class->$handler( $self, $character ); $self->{trace} and warn $class, "->$handler( \$self, '$character' )", " => (@tokens)\n"; @tokens and return ( map { ref $_ ? $_ : $self->make_token( $_, $class ) } @tokens ); } # Find a fallback processor for the character. my $fallback = __PACKAGE__->can( '__PPIX_TOKEN_FALLBACK__' . $mode ) || __PACKAGE__->can( '__PPIX_TOKEN_FALLBACK__regexp' ) || confess "Programming error - unable to find fallback for $mode"; return $fallback->( $self, $character ); } *__PPIX_TOKENIZER__repl = \&__PPIX_TOKENIZER__regexp; sub __PPIX_TOKEN_FALLBACK__regexp { my ( $self, $character ) = @_; # As a fallback in regexp mode, any escaped character is a literal. if ( $character eq '\\' && $self->{cursor_limit} - $self->{cursor_curr} > 1 ) { return $self->make_token( 2, TOKEN_LITERAL ); } # Any normal character is unknown. return $self->make_token( 1, TOKEN_UNKNOWN, { error => 'Tokenizer found unexpected literal', }, ); } sub __PPIX_TOKEN_FALLBACK__repl { my ( $self, $character ) = @_; # As a fallback in replacement mode, any escaped character is a literal. if ( $character eq '\\' && defined ( my $next = $self->peek( 1 ) ) ) { if ( $self->interpolates() || $next eq q<'> || $next eq '\\' ) { return $self->make_token( 2, TOKEN_LITERAL ); } return $self->make_token( 1, TOKEN_LITERAL ); } # So is any normal character. return $self->make_token( 1, TOKEN_LITERAL ); } sub __PPIX_TOKENIZER__finish { my ( $self ) = @_; # $character unused $self->{cursor_limit} > length $self->{content} and confess "Programming error - ran off string"; my @tokens = $self->make_token( length $self->{delimiter_finish}, 'PPIx::Regexp::Token::Delimiter' ); if ( $self->{cursor_curr} == $self->{cursor_modifiers} ) { # We are out of string. Add the trailing tokens (created when we # did the initial bracket scan) and close up shop. push @tokens, $self->_get_trailing_tokens(); $self->_set_mode( 'kaput' ); } else { # Clear the cookies, because we are going around again. $self->{cookie} = {}; # Move the cursor limit to just before the modifiers. $self->{cursor_limit} = $self->{cursor_modifiers} - 1; # If the preceding regular expression was bracketed, we need to # consume possible whitespace and find another delimiter. if ( $self->close_bracket( $self->{delimiter_start} ) ) { my $accept; # If we are bracketed, there can be honest-to-God Perl # comments between the regexp and the replacement, not just # regexp comments. As of version 1.220, PPI does not get # this parse right, but if we can handle this is a string, # then we will Just Work when PPI gets itself straight. while ( $self->find_regexp( qr{ \A ( \s* \n \s* ) ( \# [^\n]* \n ) }smx ) ) { my ( $white_space, $comment ) = $self->capture(); push @tokens, $self->make_token( length $white_space, 'PPIx::Regexp::Token::Whitespace', ), $self->make_token( length $comment, 'PPIx::Regexp::Token::Comment', ); } $accept = $self->find_regexp( qr{ \A \s+ }smx ) and push @tokens, $self->make_token( $accept, 'PPIx::Regexp::Token::Whitespace' ); my $character = $self->peek(); $self->{delimiter_start} = $character; push @tokens, $self->make_token( 1, 'PPIx::Regexp::Token::Delimiter' ); $self->{delimiter_finish} = substr $self->{content}, $self->{cursor_limit} - 1, 1; } if ( $self->modifier( 'e*' ) ) { # With /e or /ee, the replacement portion is code. We make # it all into one big PPIx::Regexp::Token::Code, slap on the # trailing delimiter and modifiers, and return it all. push @tokens, $self->make_token( $self->{cursor_limit} - $self->{cursor_curr}, 'PPIx::Regexp::Token::Code', { perl_version_introduced => MINIMUM_PERL }, ); $self->{cursor_limit} = length $self->{content}; push @tokens, $self->make_token( 1, 'PPIx::Regexp::Token::Delimiter' ), $self->_get_trailing_tokens(); $self->_set_mode( 'kaput' ); } else { # Put our mode to replacement. $self->_set_mode( 'repl' ); } } return @tokens; } # To common processing on trailing tokens. sub _get_trailing_tokens { my ( $self ) = @_; if ( $self->{index_locations} ) { # We turned off index_locations when these were created, because # they were done out of order. Fix that now. foreach my $token ( @{ $self->{trailing_tokens} } ) { $self->_update_location( $token ); } } return @{ delete $self->{trailing_tokens} }; } 1; __END__ =head1 NAME PPIx::Regexp::Tokenizer - Tokenize a regular expression =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class provides tokenization of the regular expression. =head1 METHODS This class provides the following public methods. Methods not documented here (or documented below under L) are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =head2 new my $tokenizer = PPIx::Regexp::Tokenizer->new( 'xyzzy' ); This static method instantiates the tokenizer. You must pass it the regular expression to be parsed, either as a string or as a L of some sort. You can also pass optional name/value pairs of arguments. The option names are specified B a leading dash. Supported options are: =over =item default_modifiers array_reference This argument specifies default statement modifiers. It is optional, but if specified must be an array reference. See the L L documentation for the details. =item encoding name This option specifies the encoding of the string to be tokenized. If specified, an C is done on the string (or the C of the PPI class) before it is tokenized. =item index_locations This Boolean option specifies that the locations of the generated tokens are to be computed. =item postderef boolean B. See L in L for the details. This option specifies whether the tokenizer recognizes postfix dereferencing. See the L L documentation for the details. C<$PPIx::Regexp::Tokenizer::DEFAULT_POSTDEREF> is not exported. =item strict boolean This option specifies whether tokenization should assume C is in effect. The C<'strict'> pragma was introduced in Perl 5.22, and its documentation says that it is experimental, and that there is no commitment to backward compatibility. The same applies to the tokenization produced when this option is asserted. =item trace number Specifying a positive value for this option causes a trace of the tokenization. This option is unsupported in the sense that the author reserves the right to alter it without notice. If this option is unspecified, the value comes from environment variable C (see L). If this environment variable does not exist, the default is 0. =back Undocumented options are unsupported. The returned value is the instantiated tokenizer, or C if instantiation failed. In the latter case a call to L will return the reason. =head2 content print $tokenizer->content(); This method returns the string being tokenized. This will be the result of the L<< PPI::Element->content()|PPI::Element/content >> method if the object was instantiated with a L. =head2 default_modifiers print join ', ', @{ $tokenizer->default_modifiers() }; This method returns a reference to a copy of the array passed to the C argument to L. If this argument was not used to instantiate the object, the return is a reference to an empty array. =head2 encoding This method returns the encoding of the data being parsed, if one was set when the class was instantiated; otherwise it simply returns undef. =head2 errstr my $tokenizer = PPIx::Regexp::Tokenizer->new( 'xyzzy' ) or die PPIx::Regexp::Tokenizer->errstr(); This static method returns an error description if tokenizer instantiation failed. =head2 failures print $tokenizer->failures(), " tokenization failures\n"; This method returns the number of tokenization failures encountered. A tokenization failure is represented in the output token stream by a L. =head2 modifier $tokenizer->modifier( 'x' ) and print "Tokenizing an extended regular expression\n"; This method returns true if the given modifier character was found on the end of the regular expression, and false otherwise. Starting with version 0.036_01, if the argument is a single-character modifier followed by an asterisk (intended as a wild card character), the return is the number of times that modifier appears. In this case an exception will be thrown if you specify a multi-character modifier (e.g. C<'ee*'>), or if you specify one of the match semantics modifiers (e.g. C<'a*'>). If called by an external tokenizer, this method returns true if if the given modifier was true at the current point in the tokenization. =head2 next_token my $token = $tokenizer->next_token(); This method returns the next token in the token stream, or nothing if there are no more tokens. =head2 significant This method exists simply for the convenience of L. It always returns true. =head2 tokens my @tokens = $tokenizer->tokens(); This method returns all remaining tokens in the token stream. =head1 EXTERNAL TOKENIZERS This class does very little of its own tokenization. Instead the token classes contain external tokenization routines, whose name is '__PPIX_TOKENIZER__' concatenated with the current mode of the tokenizer ('regexp' for regular expressions, 'repl' for the replacement string). These external tokenizers are called as static methods, and passed the C object and the current character in the character stream. If the external tokenizer wants to make one or more tokens, it returns an array containing either length in characters for tokens of the tokenizer's own class, or the results of one or more L calls for tokens of an arbitrary class. If the external tokenizer is not interested in the characters starting at the current position it simply returns. The following methods are for the use of external tokenizers, and B =head2 capture if ( $tokenizer->find_regexp( qr{ \A ( foo ) }smx ) ) { foreach ( $tokenizer->capture() ) { print "$_\n"; } } This method returns all the contents of any capture buffers from the previous call to L. The first element of the array (i.e. element 0) corresponds to C<$1>, and so on. The captures are cleared by L, as well as by another call to L. =head2 cookie $tokenizer->cookie( foo => sub { 1 } ); my $cookie = $tokenizer->cookie( 'foo' ); my $old_hint = $tokenizer->cookie( foo => undef ); This method either creates, deletes, or accesses a cookie. A cookie is a code reference which is called whenever the tokenizer makes a token. If it returns a false value, it is deleted. Explicitly setting the cookie to C also deletes it. When you call C<< $tokenizer->cookie( 'foo' ) >>, the current cookie is returned. If you pass a new value of C to delete the token, the deleted cookie (if any) is returned. When the L method calls a cookie, it passes it the tokenizer and the token just made. If a token calls a cookie, it is recommended that it merely pass the tokenizer, though of course the token can do whatever it wants. The cookie mechanism seems to be a bit of a crock, but it appeared to be more work to fix things up in the lexer after the tokenizer got something wrong. The recommended way to write a cookie is to use a closure to store any necessary data, and have a call to the cookie return the data; otherwise the ultimate consumer of the cookie has no way to access the data. Of course, it may be that the presence of the cookie at a certain point in the parse is all that is required. =head2 expect $tokenizer->expect( 'PPIx::Regexp::Token::Code' ); This method inserts a given class at the head of the token scan, for the next iteration only. More than one class can be specified. Class names can be abbreviated by removing the leading 'PPIx::Regexp::'. If no class is specified, this method does nothing. The expectation lasts from the next time L is called until the next time L makes a significant token, or until the next C call if that is done sooner. =head2 find_regexp my $end = $tokenizer->find_regexp( qr{ \A \w+ }smx ); my ( $begin, $end ) = $tokenizer->find_regexp( qr{ \A \w+ }smx ); This method finds the given regular expression in the content, starting at the current position. If called in scalar context, the offset from the current position to the end of the matched string is returned. If called in list context, the offsets to both the beginning and the end of the matched string are returned. =head2 find_matching_delimiter my $offset = $tokenizer->find_matching_delimiter(); This method is used by tokenizers to find the delimiter matching the character at the current position in the content string. If the delimiter is an opening bracket of some sort, bracket nesting will be taken into account. When searching for the matching delimiter, the back slash character is considered to escape the following character, so back-slashed delimiters will be ignored. No other quoting mechanisms are recognized, though, so delimiters inside quotes still count. This is actually the way Perl works, as $ perl -e 'qr<(?{ print "}" })>' demonstrates. This method returns the offset from the current position in the content string to the matching delimiter (which will always be positive), or undef if no match can be found. =head2 get_mode This method returns the name of the current mode of the tokenizer. =head2 get_start_delimiter my $start_delimiter = $tokenizer->get_start_delimiter(); This method is used by tokenizers to access the start delimiter for the regular expression. =head2 get_token my $token = $tokenizer->make_token( 3 ); my @tokens = $tokenizer->get_token(); This method returns the next token that can be made from the input stream. It is B part of the external interface, but is intended for the use of an external tokenizer which calls it after making and retaining its own token to look at the next token ( if any ) in the input stream. If any external tokenizer calls get_token without first calling make_token, a fatal error occurs; this is better than the infinite recursion which would occur if the condition were not trapped. An external tokenizer B return anything returned by get_token; otherwise tokens get lost. =head2 interpolates This method returns true if the top-level structure being tokenized interpolates; that is, if the delimiter is not a single quote. =head2 make_token return $tokenizer->make_token( 3, 'PPIx::Regexp::Token::Unknown' ); This method is used by this class (and possibly by individual tokenizers) to manufacture a token. Its arguments are the number of characters to include in the token, and optionally the class of the token. If no class name is given, the caller's class is used. Class names may be shortened by removing the initial 'PPIx::Regexp::', which will be restored by this method. The token will be manufactured from the given number of characters starting at the current cursor position, which will be adjusted. If the given length would include characters past the end of the string being tokenized, the length is reduced appropriately. If this means a token with no characters, nothing is returned. =head2 match if ( $tokenizer->find_regexp( qr{ \A \w+ }smx ) ) { print $tokenizer->match(), "\n"; } This method returns the string matched by the previous call to L. The match is set to C by L, as well as by another call to L. =head2 modifier_duplicate $tokenizer->modifier_duplicate(); This method duplicates the modifiers on the top of the modifier stack, with the intent of creating a locally-scoped copy of the modifiers. This should only be called by an external tokenizer that is actually creating a modifier scope. In other words, only when creating a L token whose content is '('. =head2 modifier_modify $tokenizer->modifier_modify( name => $value ... ); This method sets new values for the modifiers in the local scope. Only the modifiers whose names are actually passed have their values changed. This method is intended to be called after manufacturing a L token, and passed the results of its C method. =head2 modifier_pop $tokenizer->modifier_pop(); This method removes the modifiers on the top of the modifier stack. This should only be called by an external tokenizer that is ending a modifier scope. In other words, only when creating a L token whose content is ')'. Note that this method will never pop the last modifier item off the stack, to guard against unmatched right parentheses. =head2 modifier_seen $tokenizer->modifier_seen( 'i' ) and print "/i was seen at some point.\n"; Unlike L, this method returns a true value if the given modifier has been seen in any scope visible from the current location in the parse. There is no magic for group match semantics ( /a, /aa, /d, /l, /u) or modifiers that can be repeated, like /x and /xx, or /e and /ee. =head2 peek my $character = $tokenizer->peek(); my $next_char = $tokenizer->peek( 1 ); This method returns the character at the given non-negative offset from the current position. If no offset is given, an offset of 0 is used. If you ask for a negative offset or an offset off the end of the sting, C is returned. =head2 ppi_document This method makes a PPI document out of the remainder of the string, and returns it. =head2 prior_significant_token $tokenizer->prior_significant_token( 'can_be_quantified' ) and print "The prior token can be quantified.\n"; This method calls the named method on the most-recently-instantiated significant token, and returns the result. Any arguments subsequent to the method name will be passed to the method. Because this method is designed to be used within the tokenizing system, it will die horribly if the named method does not exist. If called with no arguments at all the most-recently-instantiated significant token is returned. =head2 strict say 'Parse is ', $tokenizer->strict() ? 'strict' : 'lenient'; This method simply returns true or false, depending on whether the C<'strict'> option to C was true or false. =head1 ENVIRONMENT VARIABLES A tokenizer trace can be requested by setting environment variable PPIX_REGEXP_TOKENIZER_TRACE to a numeric value other than 0. Use of this environment variable is unsupported in the same sense that the C option of L is unsupported. Explicitly specifying the C option to L overrides the environment variable. The real reason this is documented is to give the user a way to troubleshoot funny output from the tokenizer. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Util.pm000444000765000024 1157514151156043 17137 0ustar00tomstaff000000000000package PPIx::Regexp::Util; use 5.006; use strict; use warnings; use Carp; use PPIx::Regexp::Constant qw{ @CARP_NOT }; use Scalar::Util qw{ blessed }; use Text::Tabs (); use base qw{ Exporter }; our @EXPORT_OK = qw{ is_ppi_regexp_element __choose_tokenizer_class __instance __is_ppi_regexp_element __ns_can __to_ordinal_en }; our $VERSION = '0.082'; sub is_ppi_regexp_element { my ( $elem ) = @_; __instance( $elem, 'PPI::Element' ) or return; return $elem->isa( 'PPI::Token::Regexp' ) || $elem->isa( 'PPI::Token::QuoteLike::Regexp' ); } sub __is_ppi_regexp_element { Carp::cluck( '__is_ppi_regexp_element is deprecated. Use is_ppi_regexp_element' ); goto &is_ppi_regexp_element; } # TODO ditch this once the deprecation period ends sub __choose_tokenizer_class { # my ( $content, $arg ) = @_; my ( undef, $arg ) = @_; if ( defined $arg->{parse} ) { my $warning = q; { guess => 1, string => 1 }->{$arg->{parse}} and $warning = join ' ', $warning, q; croak $warning; } return 'PPIx::Regexp::Tokenizer'; } sub __instance { my ( $object, $class ) = @_; blessed( $object ) or return; return $object->isa( $class ); } sub __ns_can { my ( $class, $name ) = @_; my $fqn = join '::', ref $class || $class, $name; no strict qw{ refs }; return defined &$fqn ? \&$fqn : undef; } sub __to_ordinal_en { my ( $num ) = @_; $num += 0; 1 == int( ( $num % 100 ) / 10 ) # teens and return "${num}th"; 1 == $num % 10 and return "${num}st"; 2 == $num % 10 and return "${num}nd"; 3 == $num % 10 and return "${num}rd"; return "${num}th"; } 1; __END__ =head1 NAME PPIx::Regexp::Util - Utility functions for PPIx::Regexp; =head1 SYNOPSIS use PPIx::Regexp::Util qw{ __instance }; . . . __instance( $foo, 'Bar' ) or die '$foo is not a Bar'; =head1 DESCRIPTION This module contains utility functions for L which it is convenient to centralize. Double-underscore subroutines are B to the C package. Their documentation is provided for the author's convenience only, and they are subject to change without notice. I This module exports nothing by default. =head1 SUBROUTINES This module can export the following subroutines: =head2 is_ppi_regexp_element is_ppi_regexp_element( $elem ) and print "$elem is a regexp of some sort\n"; This subroutine is public and supported. This subroutine takes as its argument a L. It returns a true value if the argument represents a regular expression of some sort, and a false value otherwise. =head2 __instance __instance( $foo, 'Bar' ) and print '$foo isa Bar', "\n"; This subroutine is B to the C package. This subroutine returns true if its first argument is an instance of the class specified by its second argument. Unlike C, the result is always false unless the first argument is a reference. =head2 __is_ppi_regexp_element __is_ppi_regexp_element( $elem ) and print "$elem is a regexp of some sort\n"; This subroutine is B to the C package. This is a synonym for L, and is deprecated in favor of it. If called, it will complain via C and then C. =head2 __ns_can This subroutine is B to the C package. This method is analogous to C, but returns a reference to the code only if it is actually implemented by the invoking name space. =head2 __to_ordinal_en This subroutine is B to the C package. This subroutine takes as its argument an integer and returns a string representing its ordinal in English. For example say __to_ordinal_en( 17 ); # 17th =cut =head1 SEE ALSO L, which I recommend, but in the case of C I did not want to introduce a dependency on an XS module when all I really wanted was the function of that module's C<_INSTANCE()> subroutine. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2010-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Node000755000765000024 014151156043 16363 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Node/Range.pm000444000765000024 646614151156043 20126 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Node::Range - Represent a character range in a character class =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{[a-z]}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a character range in a character class. It is a node rather than a structure because there are no delimiters. The content is simply the two literals with the '-' operator between them. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Node::Range; use strict; use warnings; use base qw{ PPIx::Regexp::Node }; use PPIx::Regexp::Constant qw{ MSG_PROHIBITED_BY_STRICT @CARP_NOT }; our $VERSION = '0.082'; sub explain { my ( $self ) = @_; my $first = $self->schild( 0 ) or return $self->__no_explanation(); my $last = $self->schild( -1 ) or return $self->__no_explanation(); return sprintf q, $first->content(), $last->content(); } sub __PPIX_LEXER__finalize { my ( $self, $lexer ) = @_; my $rslt = $self->SUPER::__PPIX_LEXER__finalize( $lexer ); if ( $lexer->strict() ) { # If strict is in effect, we're an error unless both ends of the # range are portable. my @kids = $self->schildren(); delete $self->{_range_start}; # Context for compatibility. foreach my $inx ( 0, -1 ) { my $kid = $kids[$inx]; # If we're not a literal, we can not make the test, so we # blindly accept it. $kid->isa( 'PPIx::Regexp::Token::Literal' ) or next; my $content = $kid->content(); $content =~ m/ \A (?: [[:alnum:]] | \\N\{ .* \} ) \z /smx and $self->_range_ends_compatible( $content ) or return $self->_prohibited_by_strict( $rslt ); } } return $rslt; } sub _prohibited_by_strict { my ( $self, $rslt ) = @_; delete $self->{_range_start}; $rslt += $self->__error( join( ' ', 'Non-portable range ends', MSG_PROHIBITED_BY_STRICT ), perl_version_introduced => '5.023008', ); return $rslt; } sub _range_ends_compatible { my ( $self, $content ) = @_; if ( defined( my $start = $self->{_range_start} ) ) { foreach my $re ( qr{ \A [[:upper:]] \z }smx, qr{ \A [[:lower:]] \z }smx, qr{ \A [0-9] \z }smx, qr{ \A \\N \{ .* \} }smx, ) { $start =~ $re or next; return $content =~ $re; } return; } else { $self->{_range_start} = $content; return 1; } } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Node/Unknown.pm000444000765000024 317414151156043 20522 0ustar00tomstaff000000000000package PPIx::Regexp::Node::Unknown; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Node }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; 1; __END__ =head1 NAME PPIx::Regexp::Node::Unknown - Represent an unknown node. =head1 SYNOPSIS None. Sorry. This class was added to support the C option, which was itself an attempt to capture the functionality of C. It is not known to have any other use, and it cannot be instantiated in any straightforward manner. =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class is used for a node which the lexer recognizes as being improperly constructed. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2016-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure000755000765000024 014151156043 17476 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Assertion.pm000444000765000024 511714151156043 22144 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Assertion - Represent a parenthesized assertion =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?<=foo)bar}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents one of the parenthesized assertions, either look ahead or look behind, and either positive or negative. =head1 METHODS This class provides the following public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Assertion; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use Carp qw{ confess }; our $VERSION = '0.082'; use PPIx::Regexp::Constant qw{ LITERAL_LEFT_CURLY_ALLOWED @CARP_NOT }; =head2 is_look_ahead This method returns a true value if the assertion is a look-ahead assertion, or a false value if it is a look-behind assertion. =cut sub is_look_ahead { my ( $self ) = @_; return $self->_get_type()->is_look_ahead(); } =head2 is_positive This method returns a true value if the assertion is a positive assertion, or a false value if it is a negative assertion. =cut sub is_positive { my ( $self ) = @_; return $self->_get_type()->is_positive(); } # An un-escaped literal left curly bracket can always follow this # element. sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_ALLOWED; } sub _get_type { my ( $self ) = @_; my $type = $self->type() or confess 'Programming error - no type object'; $type->isa( 'PPIx::Regexp::Token::GroupType::Assertion' ) or confess 'Programming error - type object is ', ref $type, ' not PPIx::Regexp::Token::GroupType::Assertion'; return $type; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Atomic_Script_Run.pm000444000765000024 333014151156043 23554 0ustar00tomstaff000000000000package PPIx::Regexp::Structure::Atomic_Script_Run; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; 1; __END__ =head1 NAME PPIx::Regexp::Structure::Atomic_Script_Run - Represent an atomic script run group =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(*atomic_script_run:\d)}' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents an atomic script run group. That is, the constructions C<(*atomic_script_run:...)> and C<(*asr:...)>. These are new with Perl 5.27.9. If this construction does not make it into Perl 5.28, this class will be retracted. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2018-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/BranchReset.pm000444000765000024 426114151156043 22374 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::BranchReset - Represent a branch reset group =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?|(foo)|(bar))}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a branch reset group. That is, the construction C<(?|(...)|(...)|...)>. This is new with Perl 5.010. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::BranchReset; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( $self, $number ) = @_; defined $number or confess 'Programming error - initial $number is undef'; my $original = $number; my $hiwater = $number; foreach my $kid ( $self->children() ) { if ( $kid->isa( 'PPIx::Regexp::Token::Operator' ) && $kid->content() eq '|' ) { $number > $hiwater and $hiwater = $number; $number = $original; } else { $number = $kid->__PPIX_LEXER__record_capture_number( $number ); } } return $number > $hiwater ? $number : $hiwater; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Capture.pm000444000765000024 457514151156043 21607 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Capture - Represent capture parentheses. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(foo)}smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L. =head1 DESCRIPTION This class represents capture parentheses. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Structure::Capture; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub explain { my ( $self ) = @_; return sprintf q, $self->number(); } =head2 name my $name = $element->name(); This method returns the name of the capture buffer. Unless the buffer is actually named, this will be C. =cut sub name { return; } =head2 number my $number = $element->number() This method returns the number of the capture buffer. Note that named buffers have numbers also. =cut sub number { my ( $self ) = @_; return $self->{number}; } # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( $self, $number ) = @_; $self->{number} = $number++; return $self->SUPER::__PPIX_LEXER__record_capture_number( $number ); } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/CharClass.pm000444000765000024 525514151156043 22043 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::CharClass - Represent a character class =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{[fo]}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a square-bracketed character class. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Structure::CharClass; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ LITERAL_LEFT_CURLY_REMOVED_PHASE_2 @CARP_NOT }; use PPIx::Regexp::Util qw{ __instance }; our $VERSION = '0.082'; sub __new { my ( $class, @args ) = @_; ref $class and $class = ref $class; my %brkt; $brkt{finish} = pop @args; $brkt{start} = shift @args; __instance( $args[0], 'PPIx::Regexp::Token::Operator' ) and $args[0]->content() eq '^' and $brkt{type} = shift @args; return $class->SUPER::__new( \%brkt, @args ); } sub explain { my ( $self ) = @_; $self->negated() and return 'Inverted character class'; return 'Character class'; } =head2 negated $class->negated() and print "Class is negated\n"; This method returns true if the character class is negated -- that is, if the first token inside the left square bracket is a caret (C<^>). =cut sub negated { my ( $self ) = @_; return $self->type() ? 1 : 0; } sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_REMOVED_PHASE_2; } # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( undef, $number ) = @_; # Invocant unused return $number; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Code.pm000444000765000024 414614151156043 21050 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Code - Represent one of the code structures. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?{print "hello sailor\n")}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents one of the code structures, either (?{ code }) or (??{ code }) =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Code; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # The only child of this structure should be a single # PPIx::Regexp::Token::Code. Anything else gets turned into the # appropriate ::Unknown object. sub __PPIX_LEXER__finalize { my ( $self ) = @_; # $lexer unused my $count; my $errors = 0; foreach my $kid ( $self->children() ) { if ( $kid->isa( 'PPIx::Regexp::Token::Code' ) ) { $count++ or next; $errors++; $kid->__error( 'Code structure can contain only one code token' ); } else { $errors++; $kid->__error( 'Code structure may not contain a ' . ref $kid ); } } return $errors; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Main.pm000444000765000024 555014151156043 21062 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Main - Represent a regular expression proper, or a substitution =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L and L. =head1 DESCRIPTION This abstract class represents one of the top-level structures in the expression. Both L and L are derived from it. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Structure::Main; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; =head2 delimiters This method returns a string representing the delimiters of a regular expression or substitution string. In the case of something like C, it will return '//' for both the regular expression and the replacement. =cut sub delimiters { my ( $self ) = @_; my @delims; foreach my $method ( qw{ start finish } ) { push @delims, undef; defined ( my $obj = $self->$method() ) or next; defined ( my $str = $obj->content() ) or next; $delims[-1] = $str; } defined ( $delims[0] ) or $delims[0] = $delims[1]; return $delims[0] . $delims[1]; } =head2 interpolates This method returns true if the regular expression or replacement interpolates, and false otherwise. All it really does is to check whether the ending delimiter is a single quote. =cut sub interpolates { my ( $self ) = @_; my $finish = $self->finish( 0 ) or return 1; return q<'> ne $finish->content(); } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Modifier.pm000444000765000024 422314151156043 21730 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Modifier - Represent modifying parentheses =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?i:foo)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents parentheses that apply modifiers to their contents -- even if there are no modifiers. The latter is to say that C<(?:foo)> also ends up as this class. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Modifier; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # This is a kluge for both determining whether the object asserts # modifiers (hence the 'ductype') and determining whether the given # modifier is actually asserted. The signature is the invocant and the # modifier name, which must not be undef. The return is a boolean. sub __ducktype_modifier_asserted { my ( $self, $modifier ) = @_; foreach my $type ( reverse $self->type() ) { $type->can( '__ducktype_modifier_asserted' ) or next; defined( my $val = $type->__ducktype_modifier_asserted( $modifier ) ) or next; return $val; } return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/NamedCapture.pm000444000765000024 415614151156043 22547 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::NamedCapture - Represent a named capture =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?foo)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a named capture. Its content will be something like one of the following: (? ... ) (?'NAME' ... ) (?P ... ) =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Structure::NamedCapture; use strict; use warnings; use Carp; use base qw{ PPIx::Regexp::Structure::Capture }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub explain { my ( $self ) = @_; return sprintf q, $self->name(), $self->number(); } =head2 name my $name = $element->name(); This method returns the name of the capture. =cut sub name { my ( $self ) = @_; my $type = $self->type() or croak 'Programming error - ', __PACKAGE__, ' without type object'; return $type->name(); } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Quantifier.pm000444000765000024 1050014151156043 22314 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Quantifier - Represent curly bracket quantifiers =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{fo{2,}}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents curly bracket quantifiers such as C<{3}>, C<{3,}> and C<{3,5}>. The contents are left as literals or interpolations. B that if they occur inside a variable-length look-behind, quantifiers with different low and high limits (such as C<'{1,3}'> imply a minimum Perl version of C<5.29.9>. Quantifiers specifying more than 255 characters are regarded as parse errors and reblessed into the unknown structure. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Quantifier; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ LITERAL_LEFT_CURLY_ALLOWED MINIMUM_PERL MSG_LOOK_BEHIND_TOO_LONG STRUCTURE_UNKNOWN VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED @CARP_NOT }; our $VERSION = '0.082'; sub can_be_quantified { return; } sub explain { my ( $self ) = @_; my $content = $self->content(); if ( $content =~ m/ \A [{] ( .*? ) [}] \z /smx ) { my $quant = $1; my ( $lo, $hi ) = split qr{ , }smx, $quant; foreach ( $lo, $hi ) { defined or next; s/ \A \s+ //smx; s/ \s+ \z //smx; } defined $lo and '' ne $lo or $lo = '0'; defined $hi and '' ne $hi and return "match $lo to $hi times"; $quant =~ m/ , \z /smx and return "match $lo or more times"; $lo =~ m/ [^0-9] /smx and return "match $lo times"; return "match exactly $lo times"; } return $self->SUPER::explain(); } sub is_quantifier { return 1; } sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_ALLOWED; } sub _too_big { my ( $self ) = @_; STRUCTURE_UNKNOWN->__PPIX_ELEM__rebless( $self, error => MSG_LOOK_BEHIND_TOO_LONG, ); return 1; } sub __PPIX_LEXER__finalize { my ( $self ) = @_; my $content = $self->content(); if ( $self->__in_look_behind() ) { if ( $content =~ m/ \A [{] ( .*? ) [}] \z /smx ) { my $quant = $1; $quant =~ m/ , \z /smx and return $self->_too_big(); my ( $lo, $hi ) = split qr{ , }smx, $quant; defined $hi or $hi = $lo; my $numeric = 1; foreach ( $lo, $hi ) { if ( m/ \A [0-9]+ \z /smx ) { $_ >= 256 and return $self->_too_big(); } else { $numeric = 0; } } if ( $numeric && $lo != $hi ) { if ( my $finish = $self->finish() ) { $finish->perl_version_introduced() lt VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED and $finish->{perl_version_introduced} = VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED; } } # The problem I am having is that the dumper uses # __structured_requirements_for_perl(), which is not # sensitive to the minimum perl of structures, only # elements. But there is no logical element to hang the # minimum version on. Maybe the opening bracket is less bad # than the other choices? } } ( $content =~ m/ \s /smx or $content =~ m/ \A \{ , /smx ) and $self->finish()->{perl_version_introduced} = '5.033006'; return 0; } # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( undef, $number ) = @_; # Invocant unused return $number; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/RegexSet.pm000444000765000024 505014151156043 21717 0ustar00tomstaff000000000000package PPIx::Regexp::Structure::RegexSet; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ LITERAL_LEFT_CURLY_REMOVED_PHASE_2 @CARP_NOT }; our $VERSION = '0.082'; sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_REMOVED_PHASE_2; } 1; __END__ =head1 NAME PPIx::Regexp::Structure::RegexSet - Represent a regexp character set =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?[ \w - [fox] ])}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 RESTRICTION When running under Perl 5.6, the extended white space characters are not recognized as white space. =begin comment See the code in PPIx::Regexp::Token::Literal that generates $regex_set_space for the actual machinery. The reason for the restriction is that I was, for some reason, not able to get '\x{...}' to work. =end comment =head1 DESCRIPTION This class represents a regex character set. These were introduced in Perl 5.17.8, and documented as experimental and subject to change. If changes introduced in Perl result in changes in the way C parses the regular expression, C will track the change, even if they are incompatible with the previous parse. If this functionality is retracted and the syntax used for something else, C will forget completely about regex character sets. At some point, the documentation started calling these "Extended Bracketed Character Classes", and documenting them in L. =head1 METHODS This class supports no public methods over and above those supported by the superclasses. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2013-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Regexp.pm000444000765000024 614014151156043 21424 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Regexp - Represent the top-level regular expression =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the top-level regular expression. In the example given in the L, the C<{foo}> will be represented by this class. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Structure::Regexp; use strict; use warnings; use base qw{ PPIx::Regexp::Structure::Main }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub can_be_quantified { return; } =head2 capture_names foreach my $name ( $re->capture_names() ) { print "Capture name '$name'\n"; } This method returns the capture names found in the regular expression. =cut sub capture_names { my ( $self ) = @_; my %name; my $captures = $self->find( 'PPIx::Regexp::Structure::NamedCapture') or return; foreach my $grab ( @{ $captures } ) { $name{$grab->name()}++; } return ( sort keys %name ); } sub explain { return 'Regular expression'; } =head2 max_capture_number print "Highest used capture number ", $re->max_capture_number(), "\n"; This method returns the highest capture number used by the regular expression. If there are no captures, the return will be 0. =cut sub max_capture_number { my ( $self ) = @_; return $self->{max_capture_number}; } # Called by the lexer once it has done its worst to all the tokens. # Called as a method with the lexer as argument. The return is the # number of parse failures discovered when finalizing. sub __PPIX_LEXER__finalize { my ( $self, $lexer ) = @_; my $rslt = 0; foreach my $elem ( $self->elements() ) { $rslt += $elem->__PPIX_LEXER__finalize( $lexer ); } # Calculate the maximum capture group, and number all the other # capture groups along the way. $self->{max_capture_number} = $self->__PPIX_LEXER__record_capture_number( 1 ) - 1; return $rslt; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Replacement.pm000444000765000024 350614151156043 22434 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Replacement - Represent the replacement in s/// =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 's{foo}{bar}smxg' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the replacement in a substitution operation. In the example given in the L, the C<{bar}> will be represented by this class. Note that if the substitution is not bracketed (e.g. C), this structure will contain no starting delimiter. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Replacement; use strict; use warnings; use base qw{ PPIx::Regexp::Structure::Main }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub can_be_quantified { return; } sub explain { return 'Replacement string or expression'; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Script_Run.pm000444000765000024 342614151156043 22266 0ustar00tomstaff000000000000package PPIx::Regexp::Structure::Script_Run; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; 1; __END__ =head1 NAME PPIx::Regexp::Structure::Script_Run - Represent a script run group =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(*script_run:\d)}' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a script run group. That is, the construction C<(+script_run:...)>, C<(*script_run:...)>, or C<(*sr:...)>. The first form was added in Perl 5.27.8 but retracted in favor of the second ant third forms (which are equivalent) in Perl 5.27.9. If this construction does not make it into Perl 5.28, this class will be retracted. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2018-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Subexpression.pm000444000765000024 306714151156043 23050 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Subexpression - Represent an independent subexpression =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo(?>bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents an independent subexpression which must (says F) match at the current location. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Subexpression; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Switch.pm000444000765000024 602014151156043 21430 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Switch - Represent a switch =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?(1)foo|bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a switch, or conditional expression. The condition will be the first child. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Switch; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub __PPIX_LEXER__finalize { my ( $self, $lexer ) = @_; # Assume no errors. my $rslt = 0; # Number of allowed alternations not known yet. my $alternations; # If we are a valid switch, the first child is the condition. Make # sure we have a first child and that it is of the expected class. # If it is, determine how many alternations are allowed. if ( my $condition = $self->child( 0 ) ) { foreach my $class ( qw{ PPIx::Regexp::Structure::Assertion PPIx::Regexp::Structure::Code PPIx::Regexp::Token::Condition } ) { $condition->isa( $class ) or next; $alternations = $condition->content() eq '(DEFINE)' ? 0 : 1; last; } } if ( defined $alternations ) { # If we figured out how many alternations were allowed, count # them, changing surplus ones to the unknown token. foreach my $kid ( $self->children () ) { $kid->isa( 'PPIx::Regexp::Token::Operator' ) or next; $kid->content() eq '|' or next; --$alternations >= 0 and next; $kid->__error( 'Too many alternatives for switch' ); } } else { # If we could not figure out how many alternations were allowed, # it means we did not understand our condition. Rebless # ourselves to the unknown structure and count a parse failure. $self->__error( 'Switch condition not understood' ); $rslt++; } # Delegate to the superclass to finalize our children, now that we # have finished messing with them. $rslt += $self->SUPER::__PPIX_LEXER__finalize( $lexer ); return $rslt; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Structure/Unknown.pm000444000765000024 447414151156043 21641 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Structure::Unknown - Represent an unknown structure. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?(foo)bar|baz|burfle)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class is used for a structure which the lexer recognizes as being improperly constructed. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Structure::Unknown; use strict; use warnings; use base qw{ PPIx::Regexp::Structure }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub __new { my ( $class, $content, %arg ) = @_; my $self = $class->SUPER::__new( $content, %arg ); defined $arg{error} and $self->{explanation} = $self->{error} = $arg{error}; defined $arg{explanation} and $self->{explanation} = $arg{explanation}; return $self; } sub explain { my ( $self ) = @_; return $self->{explanation} || $self->SUPER::explain(); } sub __PPIX_ELEM__rebless { my ( $class, $self, %arg ) = @_; my $rslt = $class->SUPER::__PPIX_ELEM__rebless( $self, %arg ); unless ( defined( $self->{error} = $arg{error} ) ) { Carp::cluck( 'Making unknown token with no error message' ); $self->{error} = 'Unspecified error'; $rslt++; } $self->{explanation} = defined $arg{explanation} ? $arg{explanation} : $arg{error}; return $rslt; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token000755000765000024 014151156043 16556 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Assertion.pm000444000765000024 1534514151156043 21250 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Assertion - Represent a simple assertion. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{\bfoo\b}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents one of the simple assertions; that is, those that are not defined via parentheses. This includes the zero-width assertions C<^>, C<$>, C<\b>, C<\B>, C<\A>, C<\Z>, C<\z> and C<\G>, as well as: =over =item * The C<\z> assertion added in Perl 5.005, =item * The C<\K> assertion added in Perl 5.009005, =item * The C<\b{gcb}> assertion (and friends) added in Perl 5.021009. Similar braced constructions (like C<\b{foo}>) are unknown tokens. =back =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Assertion; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_LOOKAROUND_ASSERTION LITERAL_LEFT_CURLY_ALLOWED MINIMUM_PERL TOKEN_LITERAL TOKEN_UNKNOWN @CARP_NOT }; use constant KEEP_EXPLANATION => 'In s///, keep everything before the \\K'; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; my @braced_assertions = ( [ qr< \\ [bB] [{] (?: g | gcb | wb | sb ) [}] >smx, '5.021009' ], [ qr< \\ [bB] [{] (?: lb ) [}] >smx, '5.023007' ], [ qr< \\ [bB] [{] .*? [}] >smx, undef, TOKEN_UNKNOWN, { error => 'Unknown bound type' }, ], ); =head2 is_matcher This method returns a true value because an assertion actually matches something. =cut sub is_matcher { return 1; } sub perl_version_introduced { my ( $self ) = @_; return ( $self->{perl_version_introduced} ||= $self->_perl_version_introduced() ); } { my %perl_version_introduced = ( '\\K' => '5.009005', '\\z' => '5.005', ); sub _perl_version_introduced { my ( $self ) = @_; my $content = $self->content(); foreach my $item ( @braced_assertions ) { $content =~ m/ \A $item->[0] \z /smx and return $item->[1]; } return $perl_version_introduced{ $content } || MINIMUM_PERL; } } sub perl_version_removed { my ( $self ) = @_; return ( $self->{perl_version_removed} ||= $self->_perl_version_removed() ); } sub _perl_version_removed { my ( $self ) = @_; if ( '\\K' eq $self->content() ) { my $parent = $self; while ( $parent = $parent->parent() ) { $parent->isa( 'PPIx::Regexp::Structure::Assertion' ) and return '5.031003'; } } return $self->SUPER::perl_version_removed(); } { my %explanation = ( '$' => 'Assert position is at end of string or newline', '\\A' => 'Assert position is at beginning of string', '\\B' => 'Assert position is not at word/nonword boundary', '\\B{gcb}' => 'Assert position is not at grapheme cluster boundary', '\\B{g}' => 'Assert position is not at grapheme cluster boundary', '\\B{lb}' => 'Assert position is not at line boundary', '\\B{sb}' => 'Assert position is not at sentence boundary', '\\B{wb}' => 'Assert position is not at word boundary', '\\G' => 'Assert position is at pos()', '\\K' => KEEP_EXPLANATION, '\\Z' => 'Assert position is at end of string, or newline before end', '\\b' => 'Assert position is at word/nonword boundary', '\\b{gcb}' => 'Assert position is at grapheme cluster boundary', '\\b{g}' => 'Assert position is at grapheme cluster boundary', '\\b{lb}' => 'Assert position is at line boundary', '\\b{sb}' => 'Assert position is at sentence boundary', '\\b{wb}' => 'Assert position is at word boundary', '\\z' => 'Assert position is at end of string', '^' => 'Assert position is at beginning of string or after newline', ); sub __explanation { return \%explanation; } } # An un-escaped literal left curly bracket can always follow this # element. sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_ALLOWED; } # By logic we should handle '$' here. But # PPIx::Regexp::Token::Interpolation needs to process it to see if it is # a sigil. If it is not, that module is expected to make it into an # assertion. This is to try to keep the order in which the tokenizers # are called non-critical, and try to keep all processing for a # character in one place. Except for the back slash, which gets in # everywhere. # ## my %assertion = map { $_ => 1 } qw{ ^ $ }; my %assertion = map { $_ => 1 } qw{ ^ }; my %escaped = map { $_ => 1 } qw{ b B A Z z G K }; sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # Inside a character class, these are all literals. my $make = $tokenizer->cookie( COOKIE_CLASS ) ? TOKEN_LITERAL : __PACKAGE__; # '^' and '$'. Or at least '^'. See note above for '$'. $assertion{$character} and return $tokenizer->make_token( 1, $make ); $character eq '\\' or return; defined ( my $next = $tokenizer->peek( 1 ) ) or return; # Handle assertions of the form \b{gcb} and friends, introduced in # Perl 5.21.9. These are not recognized inside square bracketed # character classes, where \b is not an assertion but a backspace # character. if ( __PACKAGE__ eq $make ) { # Only outside [...] foreach my $item ( @braced_assertions ) { my $end = $tokenizer->find_regexp( qr/ \A $item->[0] /smx ) or next; $item->[2] or return $end; return $tokenizer->make_token( $end, $item->[2], $item->[3] ); } } # We special-case '\K' because it was retracted inside look-around # assertions in 5.31.3. if ( 'K' eq $next && __PACKAGE__ eq $make && $tokenizer->__cookie_exists( COOKIE_LOOKAROUND_ASSERTION ) ) { return $tokenizer->make_token( 2, $make, { perl_version_removed => '5.031003', explanation => KEEP_EXPLANATION . '; retracted inside look-around assertion', }, ); } $escaped{$next} and return $tokenizer->make_token( 2, $make ); return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Backreference.pm000444000765000024 1340114151156043 22007 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Backreference - Represent a back reference =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(foo|bar)baz\1}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents back references of all sorts, both the traditional numbered variety and the Perl 5.010 named kind. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Backreference; use strict; use warnings; use base qw{ PPIx::Regexp::Token::Reference }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL RE_CAPTURE_NAME TOKEN_LITERAL TOKEN_UNKNOWN @CARP_NOT }; use PPIx::Regexp::Util qw{ __to_ordinal_en }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { my ( $self ) = @_; $self->is_named() and return sprintf q, $self->name(); $self->is_relative() and return sprintf q, __to_ordinal_en( - $self->number() ), $self->absolute(); return sprintf q, $self->absolute(); } { my %perl_version_introduced = ( g => '5.009005', # \g1 \g-1 \g{1} \g{-1} k => '5.009005', # \k \k'name' '?' => '5.009005', # (?P=name) (PCRE/Python) ); sub perl_version_introduced { my ( $self ) = @_; return $perl_version_introduced{substr( $self->content(), 1, 1 )} || MINIMUM_PERL; } } my @external = ( # Recognition used externally [ qr{ \A \( \? P = ( @{[ RE_CAPTURE_NAME ]} ) \) }smxo, { is_named => 1 }, ], ); my @recognize_regexp = ( # recognition used internally [ qr{ \A \\ (?: # numbered (including relative) ( [0-9]+ ) | g (?: ( -? [0-9]+ ) | \{ ( -? [0-9]+ ) \} ) ) }smx, { is_named => 0 }, ], [ qr{ \A \\ (?: # named g [{] ( @{[ RE_CAPTURE_NAME ]} ) [}] | k (?: \< ( @{[ RE_CAPTURE_NAME ]} ) \> | # named with angles ' ( @{[ RE_CAPTURE_NAME ]} ) ' ) # or quotes ) }smxo, { is_named => 1 }, ], ); my %recognize = ( regexp => \@recognize_regexp, repl => [ [ qr{ \A \\ ( [0-9]+ ) }smx, { is_named => 0 } ], ], ); # This must be implemented by tokens which do not recognize themselves. # The return is a list of list references. Each list reference must # contain a regular expression that recognizes the token, and optionally # a reference to a hash to pass to make_token as the class-specific # arguments. The regular expression MUST be anchored to the beginning of # the string. sub __PPIX_TOKEN__recognize { return __PACKAGE__->isa( scalar caller ) ? ( @external, @recognize_regexp ) : ( @external ); } sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # PCRE/Python back references are handled in # PPIx::Regexp::Token::Structure, because they are parenthesized. # All the other styles are escaped. $character eq '\\' or return; foreach ( @{ $recognize{$tokenizer->get_mode()} } ) { my ( $re, $arg ) = @{ $_ }; my $accept = $tokenizer->find_regexp( $re ) or next; my %arg = ( %{ $arg }, tokenizer => $tokenizer ); return $tokenizer->make_token( $accept, __PACKAGE__, \%arg ); } return; } sub __PPIX_TOKENIZER__repl { my ( undef, $tokenizer ) = @_; # Invocant, $character unused $tokenizer->interpolates() or return; goto &__PPIX_TOKENIZER__regexp; } # Called by the lexer to disambiguate between captures, literals, and # whatever. We have to return the number of tokens reblessed to # TOKEN_UNKNOWN (i.e. either 0 or 1) because we get called after the # parse is finalized. sub __PPIX_LEXER__rebless { my ( $self, %arg ) = @_; # Handle named back references if ( $self->is_named() ) { $arg{capture_name}{$self->name()} and return 0; return $self->__error(); } # Get the absolute capture group number. my $absolute = $self->absolute(); # If it is zero or negative, we have a relateive reference to a # non-existent capture group. $absolute <= 0 and return $self->__error(); # If the absolute number is less than or equal to the maximum # capture group number, we are good. $absolute <= $arg{max_capture} and return 0; # It's not a valid capture. If it's an octal literal, rebless it so. # Note that we can't rebless single-digit numbers, since they can't # be octal literals. my $content = $self->content(); if ( $content =~ m/ \A \\ [0-7]{2,} \z /smx ) { bless $self, TOKEN_LITERAL; return 0; } # Anything else is an error. return $self->__error(); } sub __error { my ( $self, $msg ) = @_; defined $msg or $msg = 'No corresponding capture group'; $self->{error} = $msg; bless $self, TOKEN_UNKNOWN; return 1; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Backtrack.pm000444000765000024 767314151156043 21153 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Backtrack - Represent backtrack control. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(*ACCEPT)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents one of the backtrack controls. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Backtrack; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; { my %explanation = ( ACCEPT => 'Causes match to succeed at the point of the (*ACCEPT)', COMMIT => 'Causes match failure when backtracked into on failure', FAIL => 'Always fails, forcing backtrack', MARK => 'Name branches of alternation, target for (*SKIP)', PRUNE => 'Prevent backtracking past here on failure', SKIP => 'Like (*PRUNE) but also discards match to this point', THEN => 'Force next alternation on failure', ); sub explain { my ( $self ) = @_; my $verb = $self->verb(); defined( my $expl = $explanation{$verb} ) or return $self->__no_explanation(); return $expl; } my %synonym = ( '' => 'MARK', F => 'FAIL', ); =head2 arg This method returns the backtrack control argument specified by the element. This is the text after the first colon (C<':'>), or the empty string (C<''>) if none was specified. =cut sub arg { my ( $self ) = @_; my $content = $self->content(); $content =~ s/ [^:]* //smx; # ( $content =~ s/ \) //smx; return $content; } =head2 verb This method returns the backtrack control verb represented by the element. This is the text up to but not including the first colon (C<':'>) if any. If the element specifies C<''> or C<'F">, this method will return C<'MARK'> or C<'FAIL'>, respectively. =cut sub verb { my ( $self ) = @_; my $content = $self->content(); $content =~ s/ \( \* //smx; $content =~ s/ [:)] .* //smx; defined( my $syn = $synonym{$content} ) or return $content; return $syn; } } sub perl_version_introduced { return '5.009005'; } # This must be implemented by tokens which do not recognize themselves. # The return is a list of list references. Each list reference must # contain a regular expression that recognizes the token, and optionally # a reference to a hash to pass to make_token as the class-specific # arguments. The regular expression MUST be anchored to the beginning of # the string. # Note that we have to require a non-lowercase letter after the asterisk # to avoid grabbing the so-caled alpha_assertions introduced with # 5.27.9. sub __PPIX_TOKEN__recognize { return ( [ qr{ \A \( \* (?! [[:lower:]] ) [^\)]* \) }smx ] ); } # This class gets recognized by PPIx::Regexp::Token::Structure as part # of its left parenthesis processing. =begin comment sub __PPIX_TOKENIZER__regexp { my ( $class, $tokenizer, $character ) = @_; return $character eq 'x' ? 1 : 0; } =end comment =cut 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/CharClass.pm000444000765000024 465114151156043 21122 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::CharClass - Represent a character class =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{\w}smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L and L. =head1 DESCRIPTION This class represents a character class. It is not intended that this class be instantiated; it simply serves to identify a character class in the class hierarchy, and provide any common methods that might become useful. =head1 METHODS This class provides the following public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::CharClass; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; ##=head2 is_case_sensitive ## ##This method returns true if the character class is case-sensitive (that ##is, if it may match or not based on the case of the string being ##matched), false (but defined) if it is not, and simply returns (giving ##C in scalar context and an empty list in list context) if the ##case-sensitivity can not be determined. ## ##=cut ## ##sub is_case_sensitive { ## return; ##} =head2 is_matcher This method returns a true value because a character class actually matches something. =cut sub is_matcher { return 1; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Code.pm000444000765000024 1416014151156043 20145 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Code - Represent a chunk of Perl embedded in a regular expression. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?{print "hello sailor\n"})}smx')->print; =head1 INHERITANCE C is a L. C is the parent of L. =head1 DESCRIPTION This class represents a chunk of Perl code embedded in a regular expression. Specifically, it results from parsing things like (?{ code }) (??{ code }) or from the replacement side of an s///e. Technically, interpolations are also code, but they parse differently and therefore end up in a different token. This token may not appear inside a regex set (i.e. C<(?[ ... ])>. If found, it will become a C. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Code; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPI::Document; use PPIx::Regexp::Constant qw{ COOKIE_REGEX_SET LOCATION_COLUMN LOCATION_LOGICAL_LINE LOCATION_LOGICAL_FILE @CARP_NOT }; use PPIx::Regexp::Util qw{ __instance }; our $VERSION = '0.082'; use constant TOKENIZER_ARGUMENT_REQUIRED => 1; use constant VERSION_WHEN_IN_REGEX_SET => undef; sub __new { my ( $class, $content, %arg ) = @_; defined $arg{perl_version_introduced} or $arg{perl_version_introduced} = '5.005'; my $self = $class->SUPER::__new( $content, %arg ); # TODO sort this out, since Token::Interpolation is a subclass, and # those are legal in regex sets if ( $arg{tokenizer}->cookie( COOKIE_REGEX_SET ) ) { my $ver = $self->VERSION_WHEN_IN_REGEX_SET() or return $self->__error( 'Code token not valid in Regex set' ); $self->{perl_version_introduced} < $ver and $self->{perl_version_introduced} = $ver; } $arg{tokenizer}->__recognize_postderef( $self ) and $self->{perl_version_introduced} < 5.019005 and $self->{perl_version_introduced} = '5.019005'; return $self; } sub content { my ( $self ) = @_; if ( exists $self->{content} ) { return $self->{content}; } elsif ( exists $self->{ppi} ) { return ( $self->{content} = $self->{ppi}->content() ); } else { return; } } sub explain { return 'Perl expression'; } =head2 is_matcher This method returns C because a static analysis can not in general tell whether an interpolated value matches anything. =cut sub is_matcher { return undef; } ## no critic (ProhibitExplicitReturnUndef) =head2 ppi This convenience method returns the L representing the content. This document should be considered read only. B that if the location of the invocant is available the PPI document will have stuff prefixed to it to make the location of the tokens in the new document consistent with the location. This "stuff" will include at least a C<#line> directive, and maybe leading white space. =cut sub ppi { my ( $self ) = @_; if ( exists $self->{ppi} ) { return $self->{ppi}; } elsif ( exists $self->{content} ) { my $content; my $location = $self->{location}; if ( $location ) { my $fn; if( defined( $fn = $location->[LOCATION_LOGICAL_FILE] ) ) { $fn =~ s/ (?= [\\"] ) /\\/smxg; $content = qq{#line $location->[LOCATION_LOGICAL_LINE] "$fn"\n}; } else { $content = qq{#line $location->[LOCATION_LOGICAL_LINE]\n}; } $content .= ' ' x ( $location->[LOCATION_COLUMN] - 1 ); } $content .= $self->__ppi_normalize_content(); $self->{ppi} = PPI::Document->new( \$content ); if ( $location ) { # Generate locations now. $self->{ppi}->location(); # Remove the stuff we originally injected. NOTE that we can # only get away with doing this if the removal does not # invalidate the locations of the other tokens that we just # generated. my $elem; # Remove the '#line' directive if we find it $elem = $self->{ppi}->child( 0 ) and $elem->isa( 'PPI::Token::Comment' ) and $elem->content() =~ m/ \A \#line\b /smx and $elem->remove(); # Remove the white space if we find it, and if it in fact # represents only the white space we injected to get the # column numbers right. my $wid = $location->[LOCATION_COLUMN] - 1; $wid and $elem = $self->{ppi}->child( 0 ) and $elem->isa( 'PPI::Token::Whitespace' ) and $wid == length $elem->content() and $elem->remove(); } return $self->{ppi}; } else { return; } } sub __ppi_normalize_content { my ( $self ) = @_; return $self->{content}; } # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; { no warnings qw{ qw }; ## no critic (ProhibitNoWarnings) my %accept = map { $_ => 1 } qw{ $ $# @ % & * }; # Say what casts are accepted, since not all are in am # interpolation. sub __postderef_accept_cast { return \%accept; } } sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; $character eq '{' or return; my $offset = $tokenizer->find_matching_delimiter() or return; return $offset + 1; # to include the closing delimiter. } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Comment.pm000444000765000024 500214151156043 20650 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Comment - Represent a comment. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo(?#bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a comment - both parenthesized comments (i.e. C<< (?# this is a comment ) >> and the /x mode end-of-line comments. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Comment; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; sub significant { return; } sub comment { return 1; } sub explain { return 'Comment'; } # This must be implemented by tokens which do not recognize themselves. # The return is a list of list references. Each list reference must # contain a regular expression that recognizes the token, and optionally # a reference to a hash to pass to make_token as the class-specific # arguments. The regular expression MUST be anchored to the beginning of # the string. sub __PPIX_TOKEN__recognize { return ( [ qr{ \A \( \? \# [^\)]* \) }smx ] ); } # We anticipate that these tokens will be generated by other classes: # PPIx::Regexp::Token::Structure for parenthesized comments, and # PPIx::Regexp::Token::Literal for end-of-line /x mode comments. =begin comment sub __PPIX_TOKENIZER__regexp { my ( $class, $tokenizer, $character ) = @_; return $character eq 'x' ? 1 : 0; } =end comment =cut 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Condition.pm000444000765000024 722214151156043 21202 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Condition - Represent the condition of a switch =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?(1)foo|bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the condition portion of a switch or conditional expression, provided that condition is reasonably represented as a token. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Condition; use strict; use warnings; use base qw{ PPIx::Regexp::Token::Reference }; use PPIx::Regexp::Constant qw{ RE_CAPTURE_NAME @CARP_NOT }; our $VERSION = '0.082'; { my %explanation = ( '(DEFINE)' => 'Define a group to be recursed into', '(R)' => 'True if recursing', ); sub explain { my ( $self ) = @_; my $content = $self->content(); if ( defined( my $expl = $explanation{$content} ) ) { return $expl; } if ( $content =~ m/ \A [(] R /smx ) { # ) $self->is_named() and return sprintf q, $self->name(); return sprintf q, $self->absolute(); } $self->is_named() and return sprintf q, $self->name(); return sprintf q, $self->absolute(); } } sub perl_version_introduced { my ( $self ) = @_; $self->content() =~ m/ \A [(] [0-9]+ [)] \z /smx and return '5.005'; return '5.009005'; } my @recognize = ( [ qr{ \A \( (?: ( [0-9]+ ) | R ( [0-9]+ ) ) \) }smx, { is_named => 0 } ], [ qr{ \A \( R \) }smx, { is_named => 0, capture => '0' } ], [ qr{ \A \( (?: < ( @{[ RE_CAPTURE_NAME ]} ) > | ' ( @{[ RE_CAPTURE_NAME ]} ) ' | R & ( @{[ RE_CAPTURE_NAME ]} ) ) \) }smxo, { is_named => 1} ], [ qr{ \A \( DEFINE \) }smx, { is_named => 0, capture => '0' } ], ); # This must be implemented by tokens which do not recognize themselves. # The return is a list of list references. Each list reference must # contain a regular expression that recognizes the token, and optionally # a reference to a hash to pass to make_token as the class-specific # arguments. The regular expression MUST be anchored to the beginning of # the string. sub __PPIX_TOKEN__recognize { return @recognize; } # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer ) = @_; # Invocant, $character unused foreach ( @recognize ) { my ( $re, $arg ) = @{ $_ }; my $accept = $tokenizer->find_regexp( $re ) or next; return $tokenizer->make_token( $accept, __PACKAGE__, $arg ); } return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Control.pm000444000765000024 1252214151156043 20713 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Control - Case and quote control. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{\Ufoo\E}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the case and quote controls. These apply when the regular expression is compiled, changing the actual expression generated. For example print qr{\Ufoo\E}, "\n" prints (?-xism:FOO) =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Control; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ COOKIE_QUOTE MINIMUM_PERL TOKEN_LITERAL TOKEN_UNKNOWN @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; { my %explanation = ( '\\E' => 'End of interpolation control', '\\F' => 'Fold case until \\E', '\\L' => 'Lowercase until \\E', '\\Q' => 'Quote metacharacters until \\E', '\\U' => 'Uppercase until \\E', '\\l' => 'Lowercase next character', '\\u' => 'Uppercase next character', ); sub __explanation { return \%explanation; } } { my %version_introduced = ( '\\F' => '5.015008', ); sub perl_version_introduced { my ( $self ) = @_; my $content = $self->content(); defined $version_introduced{$content} and return $version_introduced{$content}; return MINIMUM_PERL; } } my %is_control = map { $_ => 1 } qw{ l u L U Q E F }; my %cookie_slot = ( Q => 'quote', E => 'end', U => 'case', L => 'case', F => 'case', ); use constant CONTROL_MASK_QUOTE => 1 << 1; my %cookie_mask = ( case => 1 << 0, end => 0, # must be 0. quote => CONTROL_MASK_QUOTE, ); sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # If we are inside a quote sequence, we want to make literals out of # all the characters we reject; otherwise we just want to return # nothing. my $in_quote = $tokenizer->cookie( COOKIE_QUOTE ) || do { my @stack = ( { mask => 0, reject => sub { return; } } ); $tokenizer->cookie( COOKIE_QUOTE, sub { return \@stack } ); }; my $cookie_stack = $in_quote->( $tokenizer ); my $reject = $cookie_stack->[-1]{reject}; # We are not interested in anything that is not escaped. $character eq '\\' or return $reject->( 1 ); # We need to see what the next character is to figure out what to # do. If there is no next character, we do not know what to call the # back slash. my $control = $tokenizer->peek( 1 ) or return $reject->( 1, TOKEN_UNKNOWN, { error => 'Trailing back slash' }, ); # We reject any escapes that do not represent controls. $is_control{$control} or return $reject->( 2 ); # Anything left gets made into a token now, to avoid its processing # by the cookie we may make. my $token = $tokenizer->make_token( 2 ); # \U, \L, and \F supersede each other, but they stack with \Q. So we # need to track that behavior, so that we know what to do when we # hit a \E. # TODO if we wanted we could actually track which (if any) of \U, \L # and \F is in effect, and make that an attribute of any literals # made. if ( my $slot = $cookie_slot{$control} ) { if ( my $mask = $cookie_mask{$slot} ) { # We need another stack entry only if the current slot # ('case' or 'quote') is not occupied unless ( $mask & $cookie_stack->[-1]{mask} ) { # Clone the previous entry. push @{ $cookie_stack }, { %{ $cookie_stack->[-1] } }; # Set the mask to show this slot is occupied $cookie_stack->[-1]{mask} |= $mask; # Code to call when this tokenizer rejects a token $cookie_stack->[-1]{reject} = ( $mask & CONTROL_MASK_QUOTE ) ? sub { my ( $size, $class ) = @_; return $tokenizer->make_token( $size, $class || TOKEN_LITERAL ); } : $cookie_stack->[0]{reject}; } # TODO if I want to try to track what controls are in effect # where. # Record the specific content of the current slot # $cookie_stack->[-1]{$slot} = $control; } else { # \E - pop data, but don't leave empty. @{ $cookie_stack } > 1 and pop @{ $cookie_stack }; } } # Return our token. return $token; } sub __PPIX_TOKENIZER__repl { my ( undef, $tokenizer ) = @_; # Invocant, $character unused $tokenizer->interpolates() and goto &__PPIX_TOKENIZER__regexp; return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Delimiter.pm000444000765000024 732714151156043 21200 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Delimiter - Represent the delimiters of the regular expression =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This token represents the delimiters of the regular expression. Since the tokenizer has to figure out where these are anyway, this class is used to give the lexer a hint about what is going on. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Delimiter; use strict; use warnings; use base qw{ PPIx::Regexp::Token::Structure }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { return 'Regular expression or replacement string delimiter'; } =head2 perl_version_introduced Experimentation with weird delimiters shows that they did not actually work until Perl 5.8.3, so we return C<'5.008003'> for such delimiters. =cut sub perl_version_introduced { my ( $self ) = @_; $self->content() =~ m/ \A [[:^ascii:]] \z /smx and return '5.008003'; return MINIMUM_PERL; } =head2 perl_version_removed Perl 5.29.0 made fatal the use of non-standalone graphemes as regular expression delimiters. Because non-characters and permanently unassigned code points are still allowed per F, I take this to mean characters that match C (i.e. combining diacritical marks). But this regular expression does not compile under Perl 5.6. So: This method returns C<'5.029'> for such delimiters B the requisite regular expression compiles. Otherwise it return C. =cut # Perl 5.29.0 disallows unassigned code points and combining code points # as delimiters. Unfortunately for me non-characters and illegal # characters are explicitly allowed. Still more unfortunately, these # match /\p{Unassigned}/. So before I match a deprecated characer, I # have to assert that the character is neither a non-character # (\p{Noncharacter_code_point}) nor an illegal Unicode character # (\P{Any}). use constant WEIRD_CHAR_RE => eval ## no critic (ProhibitStringyEval,RequireCheckingReturnValueOfEval) 'qr< (?! [\p{Noncharacter_code_point}\P{Any}] ) [\p{Unassigned}\p{Mark}] >smx'; sub perl_version_removed { my ( $self ) = @_; WEIRD_CHAR_RE and $self->content() =~ WEIRD_CHAR_RE and return '5.029'; # I respectfully disagree with Perl Best Practices on the # following. When this method is called in list context it MUST # return undef if that's the right answer, NOT an empty list. # Otherwise hash constructors have the wrong number of elements. return undef; ## no critic (ProhibitExplicitReturnUndef) } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Greediness.pm000444000765000024 517214151156043 21346 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Greediness - Represent a greediness qualifier. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo*+}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a greediness qualifier for the preceding quantifier. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Greediness; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; { my %explanation = ( '+' => 'match longest string and give nothing back', '?' => 'match shortest string first', ); sub __explanation { return \%explanation; } } my %greediness = ( '?' => MINIMUM_PERL, '+' => '5.009005', ); =head2 could_be_greediness PPIx::Regexp::Token::Greediness->could_be_greediness( '?' ); This method returns true if the given string could be a greediness indicator; that is, if it is '+' or '?'. =cut sub could_be_greediness { my ( undef, $string ) = @_; # Invocant unused return $greediness{$string}; } sub perl_version_introduced { my ( $self ) = @_; return $greediness{ $self->content() } || MINIMUM_PERL; } sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # Invocant, $char_type unused $tokenizer->prior_significant_token( 'is_quantifier' ) or return; $greediness{$character} or return; return length $character; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType.pm000444000765000024 2133314151156043 21231 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType - Represent a grouping parenthesis type. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?i:foo)}smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L, L, L, L, L, L, L, L and L. =head1 DESCRIPTION This class represents any of the magic sequences of characters that can follow an open parenthesis. This particular class is intended to be abstract. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::GroupType; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; use PPIx::Regexp::Util qw{ __ns_can }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; =head2 __defining_string my $string = $class->__defining_string(); This method is private to the C package, and is documented for the author's benefit only. It may be changed or revoked without notice. This method returns an array of strings that define the specific group type. These strings will normally start with C<'?'>. Optionally, the first returned item may be a hash reference. The only supported key is C<{suffix}>, which is a string to be suffixed to each of the regular expressions made by C<__make_group_type_matcher()> out of the defining strings, inside a C<(?= ... )>, so that it is not included in the match. This method B be overridden, unless C<__make_group_type_matcher()> is. The override B return the same thing each time, since the results of C<__make_group_type_matcher()> are cached. =cut sub __defining_string { require Carp; Carp::confess( 'Programming error - __defining_string() must be overridden' ); } =head2 __make_group_type_matcher my $hash_ref = $class->__make_group_type_matcher(); This method is private to the C package, and is documented for the author's benefit only. It may be changed or revoked without notice. This method returns a reference to a hash. The keys are regexp delimiter characters which appear in the defining strings for the group type. For each key, the value is a reference to an array of C objects, properly escaped for the key character. Key C<''> provides the regular expressions to be used if the regexp delimiter does not appear in any of the defining strings. If this method is overridden by the subclass, method C<__defining_string()> need not be, unless the overridden C<__make_group_type_matcher()> calls C<__defining_string()>. =cut sub __make_group_type_matcher { my ( $class ) = @_; my @defs = $class->__defining_string(); my $opt = ref $defs[0] ? shift @defs : {}; my $suffix = defined $opt->{suffix} ? qr/ (?= \Q$opt->{suffix}\E ) /smx : ''; my %seen; my @chars = grep { ! $seen{$_}++ } split qr{}smx, join '', @defs; my %rslt; foreach my $str ( @defs ) { push @{ $rslt{''} ||= [] }, qr{ \A \Q$str\E $suffix }smx; foreach my $chr ( @chars ) { ( my $expr = $str ) =~ s/ (?= \Q$chr\E ) /\\/smxg; push @{ $rslt{$chr} ||= [] }, qr{ \A \Q$expr\E $suffix }smx; } } return \%rslt; } =head2 __match_setup $class->__match_setup( $tokenizer ); This method is private to the C package, and is documented for the author's benefit only. It may be changed or revoked without notice. This method performs whatever setup is needed once it is determined that the given group type has been detected. This method is called only if the class matched at the current position in the string being parsed. It must perform whatever extra setup is needed for the match. It returns nothing. This method need not be overridden. The default does nothing. =cut sub __match_setup { return; } =head2 __setup_class $class->__setup_class( \%definition, \%opt ); This method is private to the C package, and is documented for the author's benefit only. It may be changed or revoked without notice. This method uses the C<%definition> hash to create the C<__defining_string()>, C, C, and C methods for the calling class. Any of these that already exist will B be replaced. The C<%definition> hash defines all the strings that specify tokens of the invoking class. You can not (unfortunately) use this mechanism if you need a regular expression to recognize a token that belongs to this class. The keys of the C<%definition> hash are strings that specify members of this class. The values are hashes that define the specific member of the class. The following values are supported: =over =item {expl} This is the explanation of the element, to be returned by the C method. =item {intro} This is the Perl version that introduced the element, as a string. The default is the value of constant L. =item {remov} This is the Perl version that removed the element, as a string. The default is C, meaning that the element is still present in the highest released version of Perl, whether development or production. =back The C<%opt> hash is optional, and defaults to the empty hash. It is used, basically, for ad-hocery. The supported keys are: =over =item {suffix} If this element is defined, the first element returned by the generated L<__defining_string()|/__defining_string> method is a hash containing this key and value. =back =cut sub __setup_class { my ( $class, $opt ) = @_; $opt ||= {}; unless ( $class->__ns_can( '__defining_string' ) ) { my $method = "${class}::__defining_string"; my @def_str = sort keys %{ $class->DEF }; defined $opt->{suffix} and unshift @def_str, { suffix => $opt->{suffix}, }; $class->DEF->{__defining_string} = \@def_str; no strict qw{ refs }; *$method = sub { my ( $self ) = @_; return @{ $self->DEF->{__defining_string} }; }; } unless ( $class->__ns_can( 'explain' ) ) { my $method = "${class}::explain"; no strict qw{ refs }; *$method = sub { my ( $self ) = @_; $DB::single = 1; return $self->DEF->{ $self->unescaped_content() }{expl}; }; } unless ( $class->__ns_can( 'perl_version_introduced' ) ) { my $method = "${class}::perl_version_introduced"; no strict qw{ refs }; *$method = sub { my ( $self ) = @_; return $self->DEF->{ $self->unescaped_content() }{intro} || MINIMUM_PERL; }; } unless ( $class->__ns_can( 'perl_version_removed' ) ) { my $method = "${class}::perl_version_removed"; no strict qw{ refs }; *$method = sub { my ( $self ) = @_; return $self->DEF->{ $self->unescaped_content() }{remov}; }; } return; } my %matcher; sub __PPIX_TOKENIZER__regexp { my ( $class, $tokenizer ) = @_; # $character unused my $mtch = $matcher{$class} ||= $class->__make_group_type_matcher(); my $re_list = $mtch->{ $tokenizer->get_start_delimiter() } || $mtch->{''}; foreach my $re ( @{ $re_list } ) { my $accept = $tokenizer->find_regexp( $re ) or next; $class->__match_setup( $tokenizer ); return $accept; } return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Interpolation.pm000444000765000024 3154514151156043 22130 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Interpolation - Represent an interpolation in the PPIx::Regexp package. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new('qr{$foo}smx')->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a variable interpolation into a regular expression. In the L the C<$foo> would be represented by an object of this class. =head1 METHODS This class provides the following public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Interpolation; use strict; use warnings; use base qw{ PPIx::Regexp::Token::Code }; use Carp qw{ confess }; use PPI::Document; use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_REGEX_SET MINIMUM_PERL TOKEN_LITERAL @CARP_NOT }; our $VERSION = '0.082'; use constant VERSION_WHEN_IN_REGEX_SET => '5.017009'; sub __new { my ( $class, $content, %arg ) = @_; defined $arg{perl_version_introduced} or $arg{perl_version_introduced} = MINIMUM_PERL; my $self = $class->SUPER::__new( $content, %arg ); return $self; } =head2 is_matcher This method returns C because a static analysis can not in general tell whether a piece of code matches anything. =cut sub is_matcher { return undef; } ## no critic (ProhibitExplicitReturnUndef) # Return true if the token can be quantified, and false otherwise # This can be quantified because it might interpolate a quantifiable # token. Of course, it might not, but we need to be permissive here. # sub can_be_quantified { return }; # We overrode this in PPIx::Regexp::Token::Code, since (?{...}) did not # appear until Perl 5.5. But interpolation has been there since the # beginning, so we have to override again. This turns out to be OK, # though, because while Regex Sets were introduced in 5.17.8, # interpolation inside them was not introduced until 5.17.9. sub perl_version_introduced { my ( $self ) = @_; return $self->{perl_version_introduced}; } # Normalize the content of an interpolation object before making it into # a PPI document. The issue here is that things like ${x} are at least # warnings outside strings, but are normal inside them. sub __ppi_normalize_content { my ( $self ) = @_; my $content; defined( $content = $self->content() ) or return $content; # NOTE: perldata gives a regexp for this, but it requires Perl 5.10. # I believe the following caputures the intent, except possibly for # various weird combinations of '::' and "'". $content =~ s/ \A ( \$ \# \$* | [\@\$] \$* ) # Sigil and possible casts [{] \s* (?: :: )* '? # per perldata ( ^? (?: \w+ (?: (?: :: | ' ) \w+ )* (?: :: )? | [[:punct:]] ) ) \s* [}] \z /$1$2/smx; return $content; } # Match the beginning of an interpolation. my $interp_re = qr{ \A (?= [\@\$]? \$ [-\w&`'+^./\\";%=~:?!\@\$<>\[\]\{\},#] | \@ [\w\{] ) }smx; # Match bracketed interpolation my $brkt_interp_re = qr{ \A (?: [\@\$] \$* [#]? \$* [\{] (?: [][\-&`'+,^./\\";%=:?\@\$<>,#] | \^? \w+ (?: :: \w+ )* ) [\}] | \@ [\{] \w+ (?: :: \w+ )* [\}] ) }smx; # We pull out the logic of finding and dealing with the interpolation # into a separate subroutine because if we fail to find an interpolation # we want to do something with the sigils. my %allow_subscript_based_on_cast_symbol = ( q<$#> => 0, q<$> => 1, q<@> => 1, ); sub _interpolation { my ( $class, $tokenizer, undef, $in_regexp ) = @_; # $character unused # If the regexp does not interpolate, bail now. $tokenizer->interpolates() or return; # If we're a bracketed interpolation, just accept it if ( my $len = $tokenizer->find_regexp( $brkt_interp_re ) ) { return $len; } # Make sure we start off plausibly defined $tokenizer->find_regexp( $interp_re ) or return; # See if PPI can figure out what we have my $doc = $tokenizer->ppi_document() or return; # Get the first statement to work on. my $stmt = $doc->find_first( 'PPI::Statement' ) or return; my @accum; # The elements of the interpolation my $allow_subscript; # Assume no subscripts allowed # Find the beginning of the interpolation my $next = $stmt->schild( 0 ) or return; # The interpolation should start with if ( $next->isa( 'PPI::Token::Symbol' ) ) { # A symbol push @accum, $next; $allow_subscript = 1; # Subscripts are allowed } elsif ( $next->isa( 'PPI::Token::Cast' ) ) { # Or a cast followed by a block push @accum, $next; $next = $next->next_sibling() or return; if ( $next->isa( 'PPI::Token::Symbol' ) ) { defined ( $allow_subscript = $allow_subscript_based_on_cast_symbol{ $accum[-1]->content() } ) or return; push @accum, $next; } elsif ( $next->isa( 'PPI::Structure::Block' ) ) { push @accum, $next; } else { return; } } elsif ( $next->isa( 'PPI::Token::ArrayIndex' ) ) { # Or an array index push @accum, $next; } else { # None others need apply. return; } # The interpolation _may_ be subscripted. If so ... { # Only accept a subscript if wanted and available $allow_subscript and $next = $next->snext_sibling() or last; # Accept an optional dereference operator. my @subscr; if ( $next->isa( 'PPI::Token::Operator' ) ) { $next->content() eq '->' or last; push @subscr, $next; $next = $next->next_sibling() or last; # postderef was introduced in 5.19.5, per perl5195delta. if ( my $deref = $tokenizer->__recognize_postderef( __PACKAGE__, $next ) ) { push @accum, @subscr, $deref; last; } } # Accept only a subscript $next->isa( 'PPI::Structure::Subscript' ) or last; # The subscript must have a closing delimiter. $next->finish() or last; # If we are in a regular expression rather than a replacement # string, screen the subscript for content, since [] could be a # character class, and {} could be a quantifier. The perlop docs # say that Perl applies undocumented heuristics subject to # change without notice to figure this out. So we do our poor # best to be heuristical and undocumented. not $in_regexp or $class->_subscript( $next ) or last; # If we got this far, accept the subscript and try for another # one. push @accum, @subscr, $next; redo; } # Compute the length of all the PPI elements accumulated, and return # it. my $length = 0; foreach ( @accum ) { $length += ref $_ ? length $_->content() : $_; } return $length; } { no warnings qw{ qw }; ## no critic (ProhibitNoWarnings) my %accept = map { $_ => 1 } qw{ $ $# @ }; sub __postderef_accept_cast { return \%accept; } } { my %allowed = ( '[' => '_square', '{' => '_curly', ); sub _subscript { my ( $class, $struct ) = @_; # We expect to have a left delimiter, which is either a '[' or a # '{'. my $left = $struct->start() or return; my $lc = $left->content(); my $handler = $allowed{$lc} or return; # We expect a single child, which is a PPI::Statement ( my @kids = $struct->schildren() ) == 1 or return; $kids[0]->isa( 'PPI::Statement' ) or return; # We expect the statement to have at least one child. ( @kids = $kids[0]->schildren() ) or return; return $class->$handler( @kids ); } } # Return true if we think a curly-bracketed subscript is really a # subscript, rather than a quantifier. # Called as $class->$handler( ... ) above sub _curly { ## no critic (ProhibitUnusedPrivateSubroutines) my ( undef, @kids ) = @_; # Invocant unused # If the first child is a word, and either it is an only child or # the next child is the fat comma operator, we accept it as a # subscript. if ( $kids[0]->isa( 'PPI::Token::Word' ) ) { @kids == 1 and return 1; $kids[1]->isa( 'PPI::Token::Operator' ) and $kids[1]->content() eq '=>' and return 1; } # If the first child is a symbol, if ( @kids && $kids[0]->isa( 'PPI::Token::Symbol' ) ) { # Accept it if it is the only child @kids == 1 and return 1; # Accept it if there are exactly two children and the second is # a subscript. @kids == 2 and $kids[1]->isa( 'PPI::Structure::Subscript' ) and return 1; } # We reject anything else. return; } # Return true if we think a square-bracketed subscript is really a # subscript, rather than a character class. # Called as $class->$handler( ... ) above sub _square { ## no critic (ProhibitUnusedPrivateSubroutines) my ( undef, @kids ) = @_; # Invocant unused # We expect to have either a number or a symbol as the first # element. $kids[0]->isa( 'PPI::Token::Number' ) and return 1; $kids[0]->isa( 'PPI::Token::Symbol' ) and return 1; # Anything else is rejected. return; } # Alternate classes for the sigils, depending on whether we are in a # character class (index 1) or not (index 0). my %sigil_alternate = ( '$' => [ 'PPIx::Regexp::Token::Assertion', TOKEN_LITERAL ], '@' => [ TOKEN_LITERAL, TOKEN_LITERAL ], ); sub __PPIX_TOKENIZER__regexp { my ( $class, $tokenizer, $character ) = @_; exists $sigil_alternate{$character} or return; if ( my $accept = $class->_interpolation( $tokenizer, $character, 1 ) ) { return $accept; } my $alternate = $sigil_alternate{$character} or return; return $tokenizer->make_token( 1, $alternate->[$tokenizer->cookie( COOKIE_CLASS ) ? 1 : 0 ] ); } sub __PPIX_TOKENIZER__repl { my ( $class, $tokenizer, $character ) = @_; exists $sigil_alternate{$character} or return; if ( my $accept = $class->_interpolation( $tokenizer, $character, 0 ) ) { return $accept; } return $tokenizer->make_token( 1, TOKEN_LITERAL ); } 1; __END__ =begin comment Interpolation notes: $ perl -E '$foo = "\\w"; $bar = 3; say qr{$foo{$bar}}' (?-xism:) white2:~/Code/perl/PPIx-Regexp.new tom 22:50:33 $ perl -E '$foo = "\\w"; $bar = 3; say qr{foo{$bar}}' (?-xism:foo{3}) white2:~/Code/perl/PPIx-Regexp.new tom 22:50:59 $ perl -E '$foo = "\\w"; $bar = 3; %foo = {baz => 42}; say qr{$foo{$bar}}' (?-xism:) white2:~/Code/perl/PPIx-Regexp.new tom 22:51:38 $ perl -E '$foo = "\\w"; $bar = 3; %foo = {baz => 42}; say qr{$foo}' (?-xism:\w) white2:~/Code/perl/PPIx-Regexp.new tom 22:51:50 $ perl -E '$foo = "\\w"; $bar = 3; %foo = {baz => 42}; say qr{$foo{baz}}' (?-xism:) white2:~/Code/perl/PPIx-Regexp.new tom 22:52:49 $ perl -E '$foo = "\\w"; $bar = 3; %foo = {baz => 42}; say qr{${foo}{baz}}' (?-xism:\w{baz}) white2:~/Code/perl/PPIx-Regexp.new tom 22:54:07 $ perl -E '$foo = "\\w"; $bar = 3; %foo = {baz => 42}; say qr{${foo}{$bar}}' (?-xism:\w{3}) The above makes me think that Perl is extremely reluctant to understand an interpolation followed by curlys as a hash dereference. In fact, only when the interpolation was what PPI calls a block was it understood at all. $ perl -E '$foo = { bar => 42 }; say qr{$foo->{bar}};' (?-xism:42) $ perl -E '$foo = { bar => 42 }; say qr{$foo->{baz}};' (?-xism:) On the other hand, Perl seems to be less reluctant to accept an explicit dereference as a hash dereference. $ perl -E '$foo = "\\w"; $bar = 3; @foo = (42); say qr{$foo}' (?-xism:\w) white2:~/Code/perl/PPIx-Regexp.new tom 22:58:20 $ perl -E '$foo = "\\w"; $bar = 3; @foo = (42); say qr{$foo[0]}' (?-xism:42) white2:~/Code/perl/PPIx-Regexp.new tom 22:58:28 $ perl -E '$foo = "\\w"; $bar = 3; @foo = (42); say qr{$foo[$bar]}' (?-xism:) white2:~/Code/perl/PPIx-Regexp.new tom 22:58:43 $ perl -E '$foo = "\\w"; $bar = 0; @foo = (42); say qr{$foo[$bar]}' (?-xism:42) The above makes it somewhat easier to get $foo[$bar] interpreted as an array dereference, but it appears to make use of information that is not available to a static analysis, such as whether $foo[$bar] exists. Actually, the above suggests a strategy: a subscript of any kind is to be accepted as a subscript if it looks like \[\d+\], \[\$foo\], \{\w+\}, or \{\$foo\}. Otherwise, accept it as a character class or a quantifier depending on the delimiter. Obviously when I bring PPI to bear I will have to keep track of '->' operators before subscripts, and shed them from the interpolation as well if the purported subscript does not pass muster. =end comment =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Literal.pm000444000765000024 4354514151156043 20700 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Literal - Represent a literal character =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a literal character, no matter how specified. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Literal; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_REGEX_SET LITERAL_LEFT_CURLY_ALLOWED LITERAL_LEFT_CURLY_REMOVED_PHASE_1 LITERAL_LEFT_CURLY_REMOVED_PHASE_2 LITERAL_LEFT_CURLY_REMOVED_PHASE_3 MINIMUM_PERL MSG_PROHIBITED_BY_STRICT TOKEN_UNKNOWN @CARP_NOT }; our $VERSION = '0.082'; sub __new { my ( $class, $content, %arg ) = @_; my $self = $class->SUPER::__new( $content, %arg ) or return; defined $arg{ordinal} and $self->{ordinal} = $arg{ordinal}; return $self; } # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { return 'Literal character'; } =head2 is_matcher This method returns a true value because a literal matches itself. =cut sub is_matcher { return 1; } sub perl_version_introduced { my ( $self ) = @_; exists $self->{perl_version_introduced} and return $self->{perl_version_introduced}; my $content = $self->content(); my $main = $self->main_structure(); $main and $content =~ m/ \A \\ N \{ /smx and not $main->interpolates() and return ( $self->{perl_version_introduced} = '5.029010' ); $content =~ m/ \A \\ o /smx and return ( $self->{perl_version_introduced} = '5.013003' ); $content =~ m/ \A \\ N [{] U [+] /smx and return ( $self->{perl_version_introduced} = '5.008' ); $content =~ m/ \A \\ x [{] /smx # } and return ( $self->{perl_version_introduced} = '5.006' ); $content =~ m/ \A \\ N /smx and return ( $self->{perl_version_introduced} = '5.006001' ); return ( $self->{perl_version_introduced} = MINIMUM_PERL ); } { my %removed = ( q<{> => sub { my ( $self ) = @_; my $prev; if ( $prev = $self->sprevious_sibling() ) { # When an unescaped left curly follows something else in # the same structure, the logic on whether it is allowed # lives, for better or worse, on the sibling. return $prev->__following_literal_left_curly_disallowed_in(); } elsif ( $prev = $self->sprevious_element() ) { # Perl 5.27.8 deprecated unescaped literal left curlys # after a left paren that introduces a group. Therefore # the left curly has no previous sibling. But the curly # is still legal at the beginning of a regex, even one # delimited by parens, so we can not return when we find # a PPIx::Regexp::Token::Delimiter, which is a subclass # of PPIx::Regexp::Token::Structure. $prev->isa( 'PPIx::Regexp::Token::Structure' ) and q<(> eq $prev->content() and not $prev->isa( 'PPIx::Regexp::Token::Delimiter' ) and return LITERAL_LEFT_CURLY_REMOVED_PHASE_3; } # When this mess started, the idea was to always allow # unescaped literal left curlies that started a regex or a # group return LITERAL_LEFT_CURLY_ALLOWED; }, ); sub perl_version_removed { my ( $self ) = @_; exists $self->{perl_version_removed} and return $self->{perl_version_removed}; my $code; return ( $self->{perl_version_removed} = ( $code = $removed{$self->content()} ) ? scalar $code->( $self ) : undef ); } } # Some characters may or may not be literals depending on whether we are # inside a character class. The following hash identifies those # characters and says what we should return when outside (index 0) or # inside (index 1) a character class, as judged by the presence of the # relevant cookie. my %double_agent = ( '.' => [ undef, 1 ], '*' => [ undef, 1 ], '?' => [ undef, 1 ], '+' => [ undef, 1 ], '-' => [ 1, undef ], '|' => [ undef, 1 ], ); # These are the characters that other external tokenizers need to see, # or at least that we need to take a closer look at. All others can be # unconditionally made into single-character literals. my %extra_ordinary = map { $_ => 1 } split qr{}smx, '$@*+?.\\(){}[]^|-#'; # $ -> Token::Interpolation, Token::Assertion # @ -> Token::Interpolation # * -> Token::Quantifier # + ? -> Token::Quantifier, Token::Greediness # . -> Token::CharClass::Simple # \ -> Token::Control, Token::CharClass::Simple, Token::Assertion, # Token::Backreference # ( ) { } [ ] -> Token::Structure # ^ -> Token::Assertion # | - -> Token::Operator my %regex_set_operator = map { $_ => 1 } qw{ & + | - ^ ! }; # The regex for the extended white space available under regex sets in # Perl 5.17.8 and in general in perl 5.17.9. I have been unable to get # this to work under Perl 5.6.2, so for that we fall back to ASCII white # space. The stringy eval is because I have been unable to get # satisfaction out of either interpolated characters (in general) or # eval-ed "\N{U+...}" (under 5.6.2) or \x{...} (ditto). # # See PPIx::Regexp::Structure::RegexSet for the documentation of this # mess. # my $white_space_re = $] >= 5.008 ? # 'qr< \\A [\\s\\N{U+0085}\\N{U+200E}\\N{U+200F}\\N{U+2028}\\N{U+2029}]+ >smx' : # 'qr< \\A \\s+ >smx'; # # RT #91798 # The above turns out to be wrong, because \s matches too many # characters. We need the following to get the right match. Note that # \cK was added experimentally in 5.17.0 and made it into 5.18. The \N{} # characters were NOT added (as I originally thought) but were simply # made characters that generated warnings when escaped, in preparation # for adding them. When they actually get added, I will have to add back # the trinary operator. Sigh. # my $white_space_re = 'qr< \A [\t\n\cK\f\r ] >smx'; # # The extended white space characters came back in Perl 5.21.1. my $white_space_re = $] >= 5.008 ? 'qr< \\A [\\t\\n\\cK\\f\\r \\N{U+0085}\\N{U+200E}\\N{U+200F}\\N{U+2028}\\N{U+2029}]+ >smx' : 'qr< \\A [\\t\\n\\cK\\f\\r ]+ >smx'; $white_space_re = eval $white_space_re; ## no critic (ProhibitStringyEval) my %regex_pass_on = map { $_ => 1 } qw{ [ ] ( ) $ \ }; sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # Invocant, $char_type unused if ( $tokenizer->cookie( COOKIE_REGEX_SET ) ) { # If we're inside a regex set no literals are allowed, but not # all characters that get here are seen as literals. $regex_set_operator{$character} and return $tokenizer->make_token( length $character, 'PPIx::Regexp::Token::Operator' ); my $accept; # As of 5.23.4, only space and horizontal tab are legal white # space inside a bracketed class inside an extended character # class $accept = $tokenizer->find_regexp( $tokenizer->cookie( COOKIE_CLASS ) ? qr{ \A [ \t] }smx : $white_space_re ) and return $tokenizer->make_token( $accept, 'PPIx::Regexp::Token::Whitespace' ); $accept = _escaped( $tokenizer, $character ) and return $accept; $regex_pass_on{$character} and return; # At this point we have a single character which is poised to be # interpreted as a literal. These are not legal in a regex set # except when also in a bracketed class. return $tokenizer->cookie( COOKIE_CLASS ) ? length $character : $tokenizer->make_token( length $character, TOKEN_UNKNOWN, { error => 'Literal not valid in Regex set', }, ); } else { # Otherwise handle the characters that may or may not be # literals depending on whether or not we are in a character # class. if ( my $class = $double_agent{$character} ) { my $inx = $tokenizer->cookie( COOKIE_CLASS ) ? 1 : 0; return $class->[$inx]; } } # If /x is in effect _and_ we are not inside a character class, \s # is whitespace, and '#' introduces a comment. Otherwise they are # both literals. if ( $tokenizer->modifier( 'x*' ) && ! $tokenizer->cookie( COOKIE_CLASS ) ) { my $accept; $accept = $tokenizer->find_regexp( $white_space_re ) and return $tokenizer->make_token( $accept, 'PPIx::Regexp::Token::Whitespace' ); $accept = $tokenizer->find_regexp( qr{ \A \# [^\n]* (?: \n | \z) }smx ) and return $tokenizer->make_token( $accept, 'PPIx::Regexp::Token::Comment' ); } elsif ( $tokenizer->modifier( 'xx' ) && $tokenizer->cookie( COOKIE_CLASS ) ) { my $accept; $accept = $tokenizer->find_regexp( qr{ \A [ \t] }smx ) and return $tokenizer->make_token( $accept, 'PPIx::Regexp::Token::Whitespace', { perl_version_introduced => '5.025009' }, ); } else { ( $character eq '#' || $character =~ m/ \A \s \z /smx ) and return 1; } my $accept; $accept = _escaped( $tokenizer, $character ) and return $accept; # All other characters which are not extra ordinary get accepted. $extra_ordinary{$character} or return 1; return; } =begin comment The following is from perlop: The character following "\c" is mapped to some other character by converting letters to upper case and then (on ASCII systems) by inverting the 7th bit (0x40). The most interesting range is from '@' to '_' (0x40 through 0x5F), resulting in a control character from 0x00 through 0x1F. A '?' maps to the DEL character. On EBCDIC systems only '@', the letters, '[', '\', ']', '^', '_' and '?' will work, resulting in 0x00 through 0x1F and 0x7F. =end comment =cut # Recognize all the escaped constructions that generate literal # characters in one gigantic regexp. Technically \1.. through \7.. are # octal literals too, but we can not disambiguate these from back # references until we know how many there are. So the lexer gets another # dirty job. { my %special = ( '\\N{}' => sub { my ( $tokenizer, $accept ) = @_; =begin comment $tokenizer->strict() or return $tokenizer->make_token( $accept, 'PPIx::Regexp::Token::NoOp', { perl_version_removed => '5.027001', }, ); return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => join( ' ', 'Empty Unicode character name', MSG_PROHIBITED_BY_STRICT ), perl_version_introduced => '5.023008', perl_version_removed => '5.027001', }, ); =end comment =cut return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => 'Empty Unicode character name', perl_version_introduced => '5.023008', perl_version_removed => '5.027001', }, ); }, '\\o{}' => sub { my ( $tokenizer, $accept ) = @_; return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => q, }, ); }, '\\x{}' => sub { my ( $tokenizer, $accept ) = @_; $tokenizer->strict() and return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => q, }, ); return $accept; }, ); sub _escaped { my ( $tokenizer, $character ) = @_; $character eq '\\' or return; if ( my $accept = $tokenizer->find_regexp( # { qr< \A \\ ( [ox] ) [{] ( [^}]* ) [}] >smx ) ) { my $match = $tokenizer->match(); my $code; $code = $special{$match} and return $code->( $tokenizer, $accept ); my ( $kind, $value ) = $tokenizer->capture(); my $invalid = { o => qr<[^0-7]>smx, x => qr<[[:^xdigit:]]>smx, }->{$kind}; $value =~ m/ $invalid /smxg # /g for pos() or return $accept; $tokenizer->strict() and return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => sprintf( 'Non-%s character in \\%s{...}', { o => 'octal', x => 'hex', }->{$kind}, $kind, ), }, ); return $tokenizer->make_token( $accept, __PACKAGE__, { ordinal => { o => sub { oct $_[0] }, x => sub { hex $_[0] }, }->{$kind}->( substr( $value, 0, pos $value ) || 0 ), }, ); } if ( my $accept = $tokenizer->find_regexp( qr< \A \\ (?: [^\w\s] | # delimiters/metas [tnrfae] | # C-style escapes 0 [01234567]{0,2} | # octal # [01234567]{1,3} | # made from backref by lexer c [][\@[:alpha:]\\^_?] | # control characters ## x (?: \{ [[:xdigit:]]* \} | [[:xdigit:]]{0,2} ) | # hex ## o [{] [01234567]+ [}] | # octal as of 5.13.3 x [[:xdigit:]]{0,2} | # hex - brackets handled above ## N (?: \{ (?: [[:alpha:]] [\w\s:()-]* | # must begin w/ alpha ## U [+] [[:xdigit:]]+ ) \} ) | # unicode N (?: [{] (?= [^0-9] ) [^\}]* [}] ) # unicode ) >smx ) ) { my $match = $tokenizer->match(); my $code; $code = $special{$match} and return $code->( $tokenizer, $accept ); return $accept; } return; } } =head2 ordinal print 'The ordinal of ', $token->content(), ' is ', $token->ordinal(), "\n"; This method returns the ordinal of the literal if it can figure it out. It is analogous to the C built-in. It will not attempt to determine the ordinal of a unicode name (C<\N{...}>) unless L has been loaded, and supports the L)> function. Instead, it will return C. Users of Perl 5.6.2 and older may be out of luck here. Unicode code points (e.g. C<\N{U+abcd}>) should work independently of L, and just return the value of C. It will never attempt to return the ordinal of an octet (C<\C{...}>) because I don't understand the syntax. =cut { my %escapes = ( '\\t' => ord "\t", '\\n' => ord "\n", '\\r' => ord "\r", '\\f' => ord "\f", '\\a' => ord "\a", '\\b' => ord "\b", '\\e' => ord "\e", '\\c?' => ord "\c?", '\\c@' => ord "\c@", '\\cA' => ord "\cA", '\\ca' => ord "\cA", '\\cB' => ord "\cB", '\\cb' => ord "\cB", '\\cC' => ord "\cC", '\\cc' => ord "\cC", '\\cD' => ord "\cD", '\\cd' => ord "\cD", '\\cE' => ord "\cE", '\\ce' => ord "\cE", '\\cF' => ord "\cF", '\\cf' => ord "\cF", '\\cG' => ord "\cG", '\\cg' => ord "\cG", '\\cH' => ord "\cH", '\\ch' => ord "\cH", '\\cI' => ord "\cI", '\\ci' => ord "\cI", '\\cJ' => ord "\cJ", '\\cj' => ord "\cJ", '\\cK' => ord "\cK", '\\ck' => ord "\cK", '\\cL' => ord "\cL", '\\cl' => ord "\cL", '\\cM' => ord "\cM", '\\cm' => ord "\cM", '\\cN' => ord "\cN", '\\cn' => ord "\cN", '\\cO' => ord "\cO", '\\co' => ord "\cO", '\\cP' => ord "\cP", '\\cp' => ord "\cP", '\\cQ' => ord "\cQ", '\\cq' => ord "\cQ", '\\cR' => ord "\cR", '\\cr' => ord "\cR", '\\cS' => ord "\cS", '\\cs' => ord "\cS", '\\cT' => ord "\cT", '\\ct' => ord "\cT", '\\cU' => ord "\cU", '\\cu' => ord "\cU", '\\cV' => ord "\cV", '\\cv' => ord "\cV", '\\cW' => ord "\cW", '\\cw' => ord "\cW", '\\cX' => ord "\cX", '\\cx' => ord "\cX", '\\cY' => ord "\cY", '\\cy' => ord "\cY", '\\cZ' => ord "\cZ", '\\cz' => ord "\cZ", '\\c[' => ord "\c[", '\\c\\\\' => ord "\c\\", # " # Get Vim's head straight. '\\c]' => ord "\c]", '\\c^' => ord "\c^", '\\c_' => ord "\c_", ); sub ordinal { my ( $self ) = @_; exists $self->{ordinal} and return $self->{ordinal}; return ( $self->{ordinal} = $self->_ordinal() ); } my %octal = map {; "$_" => 1 } ( 0 .. 7 ); sub _ordinal { my ( $self ) = @_; my $content = $self->content(); $content =~ m/ \A \\ /smx or return ord $content; exists $escapes{$content} and return $escapes{$content}; my $indicator = substr $content, 1, 1; $octal{$indicator} and return oct substr $content, 1; if ( $indicator eq 'x' ) { $content =~ m/ \A \\ x \{ ( [[:xdigit:]]* ) /smx and return hex "0$1"; $content =~ m/ \A \\ x ( [[:xdigit:]]{0,2} ) \z /smx and return hex $1; return; } if ( $indicator eq 'o' ) { $content =~ m/ \A \\ o [{] ( [01234567]* ) \z /smx and return oct "0$1"; return; # Shouldn't happen, but ... } if ( $indicator eq 'N' ) { $content =~ m/ \A \\ N \{ U [+] ( [[:xdigit:]]+ ) \} \z /smx and return hex $1; $content =~ m/ \A \\ N [{] ( .+ ) [}] \z /smx and return ( _have_charnames_vianame() ? charnames::vianame( $1 ) : undef ); return; # Shouldn't happen, but ... } return ord $indicator; } } sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_REMOVED_PHASE_2; } { my $have_charnames_vianame; sub _have_charnames_vianame { defined $have_charnames_vianame and return $have_charnames_vianame; return ( $have_charnames_vianame = charnames->can( 'vianame' ) ? 1 : 0 ); } } sub __perl_requirements_setup { my ( $self ) = @_; my $prev; q<{> eq $self->content() # } and $prev = $self->sprevious_sibling() and $prev->isa( 'PPIx::Regexp::Token::Literal' ) or return $self->SUPER::__perl_requirements_setup(); return ( { introduced => MINIMUM_PERL, removed => LITERAL_LEFT_CURLY_REMOVED_PHASE_1, }, # TODO the following will be needed if this construction is # re-allowed in 5.26.1: # { # introduced => '5.026001', # removed => '6.027000', # }, { introduced => '5.027001', removed => LITERAL_LEFT_CURLY_REMOVED_PHASE_2, }, ); } *__PPIX_TOKENIZER__repl = \&__PPIX_TOKENIZER__regexp; 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Modifier.pm000444000765000024 4051414151156043 21033 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Modifier - Represent modifiers. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo}smx' ) ->print(); The trailing C will be represented by this class. This class also represents the whole of things like C<(?ismx)>. But the modifiers in something like C<(?i:foo)> are represented by a L. =head1 INHERITANCE C is a L. C is the parent of L. =head1 DESCRIPTION This class represents modifier characters at the end of the regular expression. For example, in C this class would represent the terminal C. =head2 The C, C, C, C, and C modifiers The C, C, C, C, and C modifiers, introduced starting in Perl 5.13.6, are used to force either Unicode pattern semantics (C), locale semantics (C) default semantics (C the traditional Perl semantics, which can also mean 'dual' since it means Unicode if the string's UTF-8 bit is on, and locale if the UTF-8 bit is off), or restricted default semantics (C). These are mutually exclusive, and only one can be asserted at a time. Asserting any of these overrides the inherited value of any of the others. The C method reports as asserted the last one it sees, or none of them if it has seen none. For example, given C C<$elem> representing the invalid regular expression fragment C<(?dul)>, C<< $elem->asserted( 'l' ) >> would return true, but C<< $elem->asserted( 'u' ) >> would return false. Note that C<< $elem->negated( 'u' ) >> would also return false, since C is not explicitly negated. If C<$elem> represented regular expression fragment C<(?i)>, C<< $elem->asserted( 'd' ) >> would return false, since even though C represents the default behavior it is not explicitly asserted. =head2 The caret (C<^>) modifier Calling C<^> a modifier is a bit of a misnomer. The C<(?^...)> construction was introduced in Perl 5.13.6, to prevent the inheritance of modifiers. The documentation calls the caret a shorthand equivalent for C, and that it the way this class handles it. For example, given C C<$elem> representing regular expression fragment C<(?^i)>, C<< $elem->asserts( 'd' ) >> would return true, since in the absence of an explicit C or C this class considers the C<^> to explicitly assert C. The caret handling is complicated by the fact that the C<'n'> modifier was introduced in 5.21.8, at which point the caret became equivalent to C. I did not feel I could unconditionally add the C<-n> to the expansion of the caret, because that would produce confusing output from methods like L. Nor could I make it conditional on the minimum perl version, because that information is not available early enough in the parse. What I did was to expand the caret into C if and only if C<'n'> was in effect at some point in the scope in which the modifier was parsed. Continuing the above example, C<< $elem->asserts( 'n' ) >> and C<< $elem->modifier_asserted( 'n' ) >> would both return false, but C<< $elem->negates( 'n' ) >> would return true if and only if the C modifier has been asserted somewhere before and in-scope from this token. The L method is inherited from L. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Modifier; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use Carp; use PPIx::Regexp::Constant qw{ MINIMUM_PERL MODIFIER_GROUP_MATCH_SEMANTICS @CARP_NOT }; our $VERSION = '0.082'; # Define modifiers that are to be aggregated internally for ease of # computation. my %aggregate = ( a => MODIFIER_GROUP_MATCH_SEMANTICS, aa => MODIFIER_GROUP_MATCH_SEMANTICS, d => MODIFIER_GROUP_MATCH_SEMANTICS, l => MODIFIER_GROUP_MATCH_SEMANTICS, u => MODIFIER_GROUP_MATCH_SEMANTICS, ); my %de_aggregate; foreach my $value ( values %aggregate ) { $de_aggregate{$value}++; } # Note that we do NOT want the /o modifier on regexen that make use of # this, because it is already compiled. my $capture_group_leader = qr{ [?/(] }smx; # ); use constant TOKENIZER_ARGUMENT_REQUIRED => 1; sub __new { my ( $class, $content, %arg ) = @_; my $self = $class->SUPER::__new( $content, %arg ) or return; $content =~ m{ \A $capture_group_leader* \^ }smx # no /o! and defined $arg{tokenizer}->modifier_seen( 'n' ) and $self->{__caret_undoes_n} = 1; $arg{tokenizer}->modifier_modify( $self->modifiers() ); return $self; } =head2 asserts $token->asserts( 'i' ) and print "token asserts i"; foreach ( $token->asserts() ) { print "token asserts $_\n" } This method returns true if the token explicitly asserts the given modifier. The example would return true for the modifier in C<(?i:foo)>, but false for C<(?-i:foo)>. Starting with version 0.036_01, if the argument is a single-character modifier followed by an asterisk (intended as a wild card character), the return is the number of times that modifier appears. In this case an exception will be thrown if you specify a multi-character modifier (e.g. C<'ee*'>). If called without an argument, or with an undef argument, all modifiers explicitly asserted by this token are returned. =cut sub asserts { my ( $self, $modifier ) = @_; $self->{modifiers} ||= $self->_decode(); if ( defined $modifier ) { return __asserts( $self->{modifiers}, $modifier ); } else { return ( sort grep { defined $_ && $self->{modifiers}{$_} } map { $de_aggregate{$_} ? $self->{modifiers}{$_} : $_ } keys %{ $self->{modifiers} } ); } } # This is a kluge for both determining whether the object asserts # modifiers (hence the 'ductype') and determining whether the given # modifier is actually asserted. The signature is the invocant and the # modifier name, which must not be undef. The return is a boolean. *__ducktype_modifier_asserted = \&asserts; sub __asserts { my ( $present, $modifier ) = @_; my $wild = $modifier =~ s/ [*] \z //smx; not $wild or 1 == length $modifier or croak "Can not use wild card on multi-character modifier '$modifier*'"; if ( my $bin = $aggregate{$modifier} ) { my $aggr = $present->{$bin}; $wild or return ( defined $aggr && $modifier eq $aggr ); defined $aggr or return 0; $aggr =~ m/ \A ( (?: \Q$modifier\E )* ) \z /smx or return 0; return length $1; } if ( $wild ) { return $present->{$modifier} || 0; } my $len = length $modifier; $modifier = substr $modifier, 0, 1; return $present->{$modifier} && $len == $present->{$modifier}; } sub can_be_quantified { return }; { my %explanation = ( 'm' => 'm: ^ and $ match within string', '-m' => '-m: ^ and $ match only at ends of string', 's' => 's: . can match newline', '-s' => '-s: . can not match newline', 'i' => 'i: do case-insensitive matching', '-i' => '-i: do case-sensitive matching', 'x' => 'x: ignore whitespace and comments', 'xx' => 'xx: ignore whitespace even in bracketed character classes', '-x' => '-x: regard whitespace as literal', 'p' => 'p: provide ${^PREMATCH} etc (pre 5.20)', '-p' => '-p: no ${^PREMATCH} etc (pre 5.20)', 'a' => 'a: restrict non-Unicode classes to ASCII', 'aa' => 'aa: restrict non-Unicode classes & ASCII-Unicode matches', 'd' => 'd: match using default semantics', 'l' => 'l: match using locale semantics', 'u' => 'u: match using Unicode semantics', 'n' => 'n: parentheses do not capture', '-n' => '-n: parentheses capture', 'c' => 'c: preserve current position on match failure', 'g' => 'g: match repeatedly', 'e' => 'e: substitution string is an expression', 'ee' => 'ee: substitution is expression to eval()', 'o' => 'o: only interpolate once', 'r' => 'r: aubstitution returns modified string', ); sub explain { my ( $self ) = @_; my @rslt; my %mods = $self->modifiers(); if ( defined( my $val = delete $mods{match_semantics} ) ) { push @rslt, $explanation{$val}; } foreach my $key ( sort keys %mods ) { if ( my $val = $mods{$key} ) { push @rslt, $explanation{ $key x $val }; } else { push @rslt, $explanation{ "-$key" }; } } return wantarray ? @rslt : join '; ', @rslt; } } =head2 match_semantics my $sem = $token->match_semantics(); defined $sem or $sem = 'undefined'; print "This token has $sem match semantics\n"; This method returns the match semantics asserted by the token, as one of the strings C<'a'>, C<'aa'>, C<'d'>, C<'l'>, or C<'u'>. If no explicit match semantics are asserted, this method returns C. =cut sub match_semantics { my ( $self ) = @_; $self->{modifiers} ||= $self->_decode(); return $self->{modifiers}{ MODIFIER_GROUP_MATCH_SEMANTICS() }; } =head2 modifiers my %mods = $token->modifiers(); Returns all modifiers asserted or negated by this token, and the values set (true for asserted, false for negated). If called in scalar context, returns a reference to a hash containing the values. =cut sub modifiers { my ( $self ) = @_; $self->{modifiers} ||= $self->_decode(); my %mods = %{ $self->{modifiers} }; foreach my $bin ( keys %de_aggregate ) { defined ( my $val = delete $mods{$bin} ) or next; $mods{$bin} = $val; } return wantarray ? %mods : \%mods; } =head2 negates $token->negates( 'i' ) and print "token negates i\n"; foreach ( $token->negates() ) { print "token negates $_\n" } This method returns true if the token explicitly negates the given modifier. The example would return true for the modifier in C<(?-i:foo)>, but false for C<(?i:foo)>. If called without an argument, or with an undef argument, all modifiers explicitly negated by this token are returned. =cut sub negates { my ( $self, $modifier ) = @_; $self->{modifiers} ||= $self->_decode(); # Note that since the values of hash entries that represent # aggregated modifiers will never be false (at least, not unless '0' # becomes a modifier) we need no special logic to handle them. defined $modifier or return ( sort grep { ! $self->{modifiers}{$_} } keys %{ $self->{modifiers} } ); return exists $self->{modifiers}{$modifier} && ! $self->{modifiers}{$modifier}; } sub perl_version_introduced { my ( $self ) = @_; return ( $self->{perl_version_introduced} ||= $self->_perl_version_introduced() ); } sub _perl_version_introduced { my ( $self ) = @_; my $content = $self->content(); my $is_statement_modifier = ( $content !~ m/ \A [(]? [?] /smx ); my $match_semantics = $self->match_semantics(); $self->asserts( 'xx' ) and return '5.025009'; # Disabling capture with /n was introduced in 5.21.8 $self->asserts( 'n' ) and return '5.021008'; # Match semantics modifiers became available as regular expression # modifiers in 5.13.10. defined $match_semantics and $is_statement_modifier and return '5.013010'; # /aa was introduced in 5.13.10. defined $match_semantics and 'aa' eq $match_semantics and return '5.013010'; # /a was introduced in 5.13.9, but only in (?...), not as modifier # of the entire regular expression. defined $match_semantics and not $is_statement_modifier and 'a' eq $match_semantics and return '5.013009'; # /d, /l, and /u were introduced in 5.13.6, but only in (?...), not # as modifiers of the entire regular expression. defined $match_semantics and not $is_statement_modifier and return '5.013006'; # The '^' reassert-defaults modifier in embedded modifiers was # introduced in 5.13.6. not $is_statement_modifier and $content =~ m/ \^ /smx and return '5.013006'; $self->asserts( 'r' ) and return '5.013002'; $self->asserts( 'p' ) and return '5.009005'; $self->content() =~ m/ \A [(]? [?] .* - /smx and return '5.005'; $self->asserts( 'c' ) and return '5.004'; return MINIMUM_PERL; } # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; # $present => __aggregate_modifiers( 'modifiers', ... ); # # This subroutine is private to the PPIx::Regexp package. It may change # or be retracted without notice. Its purpose is to support defaulted # modifiers. # # Aggregate the given modifiers left-to-right, returning a hash of those # present and their values. sub __aggregate_modifiers { my ( @mods ) = @_; my %present; foreach my $content ( @mods ) { $content =~ s{ \A $capture_group_leader+ }{}smxg; # no /o! if ( $content =~ m/ \A \^ /smx ) { @present{ MODIFIER_GROUP_MATCH_SEMANTICS(), qw{ i s m x } } = qw{ d 0 0 0 0 }; } # Have to do the global match rather than a split, because the # expression modifiers come through here too, and we need to # distinguish between s/.../.../e and s/.../.../ee. But the # modifiers can be randomized (that is, /eie is the same as # /eei), so we reorder the content first. # The following line is WRONG because it ignores the # significance of '-'. This bug was introduced in version 0.035, # specifically by the change that handled multi-character # modifiers. # $content = join '', sort split qr{}smx, $content; # The following is better because it re-orders the modifiers # separately. It does not recognize multiple dashes as # representing an error (though it could!), and modifiers that # are both asserted and negated (e.g. '(?i-i:foo)') are simply # considered to be negated (as Perl does as of 5.20.0). $content = join '-', map { join '', sort split qr{}smx } split qr{-}smx, $content; my $value = 1; while ( $content =~ m/ ( ( [[:alpha:]-] ) \2* ) /smxg ) { if ( '-' eq $1 ) { $value = 0; } elsif ( my $bin = $aggregate{$1} ) { # Yes, technically the match semantics stuff can't be # negated in a regex. But it can in a 'use re', which # also comes through here, so we have to handle it. $present{$bin} = $value ? $1 : undef; } else { # TODO have to think about this, since I need asserts( # 'e' ) to be 2 if we in fact have 'ee'. Is this # correct? # $present{$1} = $value; $present{$2} = $value * length $1; } } } return \%present; } # This must be implemented by tokens which do not recognize themselves. # The return is a list of list references. Each list reference must # contain a regular expression that recognizes the token, and optionally # a reference to a hash to pass to make_token as the class-specific # arguments. The regular expression MUST be anchored to the beginning of # the string. sub __PPIX_TOKEN__recognize { return ( [ qr{ \A [(] [?] [[:lower:]]* -? [[:lower:]]* [)] }smx ], [ qr{ \A [(] [?] \^ [[:lower:]]* [)] }smx ], ); } { # Called by the tokenizer to modify the current modifiers with a new # set. Both are passed as hash references, and a reference to the # new hash is returned. sub __PPIX_TOKENIZER__modifier_modify { my ( @args ) = @_; my %merged; foreach my $hash ( @args ) { while ( my ( $key, $val ) = each %{ $hash } ) { if ( $val ) { $merged{$key} = $val; } else { delete $merged{$key}; } } } return \%merged; } # Decode modifiers from the content of the token. sub _decode { my ( $self ) = @_; my $mod = __aggregate_modifiers( $self->content() ); $self->{__caret_undoes_n} and $mod->{n} = 0; return $mod; } } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/NoOp.pm000444000765000024 406014151156043 20124 0ustar00tomstaff000000000000package PPIx::Regexp::Token::NoOp; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use Carp; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; { my %when_removed = ( '\\N{}' => '5.027001', ); sub __new { my ( $class, $content, %arg ) = @_; defined $arg{perl_version_introduced} or $arg{perl_version_introduced} = MINIMUM_PERL; exists $arg{perl_version_removed} or $arg{perl_version_removed} = $when_removed{$content}; return $class->SUPER::__new( $content, %arg ); } } # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; sub explain { return 'Not significant'; } sub significant { return; } 1; __END__ =head1 NAME PPIx::Regexp::Token::NoOp - Represent a token that does nothing. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr< \N{} >smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L. =head1 DESCRIPTION This class represents a token the does nothing. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2016-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Operator.pm000444000765000024 1201414151156043 21062 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Operator - Represent an operator. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo|bar}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents an operator. In a character class, it represents the negation (C<^>) and range (C<->) operators. Outside a character class, it represents the alternation (C<|>) operator. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Operator; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_REGEX_SET LITERAL_LEFT_CURLY_ALLOWED TOKEN_LITERAL @CARP_NOT }; use PPIx::Regexp::Util qw{ __instance }; our $VERSION = '0.082'; use constant TOKENIZER_ARGUMENT_REQUIRED => 1; sub __new { my ( $class, $content, %arg ) = @_; my $self = $class->SUPER::__new( $content, %arg ) or return; $self->{operation} = $self->_compute_operation_name( $arg{tokenizer} ) || 'unknown'; return $self; } # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { my ( $self ) = @_; my $expl = ucfirst "$self->{operation} operator"; $expl =~ s/ _ / /smxg; return $expl; } =head2 operation This method returns the name of the operation performed by the operator. This depends not only on the operator itself but its context: =over =item In a bracketed character class '-' => 'range', '^' => 'inversion', =item In an extended bracketed character class '&' => 'intersection', '+' => 'union', '|' => 'union', '-' => 'subtraction', '^' => 'symmetric_difference', '!' => 'complement', =item Outside any sort of bracketed character class '|' => 'alternation', =back =cut sub operation { my ( $self ) = @_; return $self->{operation}; } # These will be intercepted by PPIx::Regexp::Token::Literal if they are # really literals, so here we may process them unconditionally. # Note that if we receive a '-' we unconditionally make it an operator, # relying on the lexer to turn it back into a literal if necessary. my %operator = map { $_ => 1 } qw{ | - }; sub _treat_as_literal { my ( $token ) = @_; return __instance( $token, 'PPIx::Regexp::Token::Literal' ) || __instance( $token, 'PPIx::Regexp::Token::Interpolation' ); } { my %operation = ( COOKIE_CLASS() => { '-' => 'range', '^' => 'inversion', }, COOKIE_REGEX_SET() => { '&' => 'intersection', '+' => 'union', '|' => 'union', '-' => 'subtraction', '^' => 'symmetric_difference', '!' => 'complement', }, '' => { '|' => 'alternation', }, ); sub _compute_operation_name { my ( $self, $tokenizer ) = @_; my $content = $self->content(); if ( $tokenizer->cookie( COOKIE_CLASS ) ) { return $operation{ COOKIE_CLASS() }{$content}; } elsif ( $tokenizer->cookie( COOKIE_REGEX_SET ) ) { return $operation{ COOKIE_REGEX_SET() }{$content}; } else { return $operation{''}{$content}; } } } { my $removed_in = { '|' => LITERAL_LEFT_CURLY_ALLOWED, # Allowed after alternation }; sub __following_literal_left_curly_disallowed_in { my ( $self ) = @_; my $content = $self->content(); exists $removed_in->{$content} and return $removed_in->{$content}; return $self->SUPER::__following_literal_left_curly_disallowed_in(); } } sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # We only receive the '-' if we are inside a character class. But it # is only an operator if it is preceded and followed by literals. We # can use prior() because there are no insignificant tokens inside a # character class. if ( $character eq '-' ) { _treat_as_literal( $tokenizer->prior_significant_token() ) or return $tokenizer->make_token( 1, TOKEN_LITERAL ); my @tokens = ( $tokenizer->make_token( 1 ) ); push @tokens, $tokenizer->get_token(); _treat_as_literal( $tokens[1] ) or bless $tokens[0], TOKEN_LITERAL; return ( @tokens ); } return $operator{$character}; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Quantifier.pm000444000765000024 660414151156043 21366 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Quantifier - Represent an atomic quantifier. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{\w+}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents an atomic quantifier; that is, one of the characters C<*>, C<+>, or C. B that if they occur inside a variable-length look-behind, C<'?'> implies a minimum Perl version of C<5.29.9>, and C<'+'> and C<'*'> are regarded as parse errors and reblessed into the unknown token. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Quantifier; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ LITERAL_LEFT_CURLY_ALLOWED MSG_LOOK_BEHIND_TOO_LONG TOKEN_UNKNOWN VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; # Return true if the token is a quantifier. sub is_quantifier { return 1 }; my %quantifier = map { $_ => 1 } qw{ * + ? }; =head2 could_be_quantifier PPIx::Regexp::Token::Quantifier->could_be_quantifier( '*' ); This method returns true if the given string could be a quantifier; that is, if it is '*', '+', or '?'. =cut sub could_be_quantifier { my ( undef, $string ) = @_; # Invocant unused return $quantifier{$string}; } { my %explanation = ( '*' => 'match zero or more times', '+' => 'match one or more times', '?' => 'match zero or one time', ); sub __explanation { return \%explanation; } } sub __following_literal_left_curly_disallowed_in { return LITERAL_LEFT_CURLY_ALLOWED; } { my $variable_look_behind_introduced = { '*' => undef, '+' => undef, '?' => VARIABLE_LENGTH_LOOK_BEHIND_INTRODUCED, }; sub __PPIX_LEXER__finalize { my ( $self ) = @_; if ( $self->__in_look_behind() ) { $self->{perl_version_introduced} = $variable_look_behind_introduced->{$self->content()} and return 0; TOKEN_UNKNOWN->__PPIX_ELEM__rebless( $self, error => MSG_LOOK_BEHIND_TOO_LONG, ); return 1; } return 0; } } sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; $tokenizer->prior_significant_token( 'can_be_quantified' ) or return; return $quantifier{$character}; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Recursion.pm000444000765000024 605614151156043 21231 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Recursion - Represent a recursion =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(foo(?1)?)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a recursion to a named or numbered capture. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Recursion; use strict; use warnings; use base qw{ PPIx::Regexp::Token::Reference }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ RE_CAPTURE_NAME @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { my ( $self ) = @_; $self->is_named() and return sprintf q, $self->name(); if ( $self->is_relative() ) { my $number = $self->number(); $number >= 0 and return sprintf q, PPIx::Regexp::Util::__to_ordinal_en( $self->number() ), $self->absolute(); return sprintf q, PPIx::Regexp::Util::__to_ordinal_en( - $self->number() ), $self->absolute(); } elsif ( my $number = $self->absolute() ) { return sprintf q, $number; } else { return q; } } sub perl_version_introduced { return '5.009005'; } # This must be implemented by tokens which do not recognize themselves. # The return is a list of list references. Each list reference must # contain a regular expression that recognizes the token, and optionally # a reference to a hash to pass to make_token as the class-specific # arguments. The regular expression MUST be anchored to the beginning of # the string. sub __PPIX_TOKEN__recognize { return ( [ qr{ \A \( \? (?: ( [-+]? [0-9]+ )) \) }smx, { is_named => 0 } ], [ qr{ \A \( \? (?: R) \) }smx, { is_named => 0, capture => '0' } ], [ qr{ \A \( \? (?: & | P> ) ( @{[ RE_CAPTURE_NAME ]} ) \) }smxo, { is_named => 1 } ], ); } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Reference.pm000444000765000024 1211314151156043 21165 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Reference - Represent a reference to a capture =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{\1}smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L, L and L. =head1 DESCRIPTION This abstract class represents a reference to a capture buffer, either numbered or named. It should never be instantiated, but it provides a number of methods to its subclasses. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Reference; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use Carp qw{ confess }; use List::Util qw{ first }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub __new { my ( $class, $content, %arg ) = @_; if ( defined $arg{capture} ) { } elsif ( defined $arg{tokenizer} ) { $arg{capture} = first { defined $_ } $arg{tokenizer}->capture(); } unless ( defined $arg{capture} ) { foreach ( $class->__PPIX_TOKEN__recognize() ) { my ( $re, $a ) = @{ $_ }; $content =~ $re or next; @arg{ keys %{ $a } } = @{ $a }{ keys %{ $a } }; foreach my $inx ( 1 .. $#- ) { defined $-[$inx] or next; $arg{capture} = substr $content, $-[$inx], $+[$inx] - $-[$inx]; last; } last; } } defined $arg{capture} or confess q{Programming error - reference '}, $content, q{' of unknown form}; my $self = $class->SUPER::__new( $content, %arg ) or return; $self->{is_named} = $arg{is_named}; my $capture = delete $arg{capture}; if ( $self->{is_named} ) { $self->{absolute} = undef; $self->{is_relative} = undef; $self->{name} = $capture; } elsif ( $capture !~ m/ \A [-+] /smx ) { $self->{absolute} = $self->{number} = $capture; $self->{is_relative} = undef; } else { $self->{number} = $capture; $self->{is_relative} = 1; } return $self; } =head2 absolute print "The absolute reference is ", $ref->absolute(), "\n"; This method returns the absolute number of the capture buffer referred to. This is the same as number() for unsigned numeric references. If the reference is to a named buffer, C is returned. =cut sub absolute { my ( $self ) = @_; return $self->{absolute}; } =head2 is_named $ref->is_named and print "named reference\n"; This method returns true if the reference is named rather than numbered. =cut sub is_named { my ( $self ) = @_; return $self->{is_named}; } =head2 is_relative $ref->is_relative() and print "relative numbered reference\n"; This method returns true if the reference is numbered and it is a relative number (i.e. if it is signed). =cut sub is_relative { my ( $self ) = @_; return $self->{is_relative}; } =head2 is_matcher This method returns a true value because, although we do not actually perform an analysis on the referred-to entity, we presume it matches something. =cut sub is_matcher { return 1; } =head2 name print "The name is ", $ref->name(), "\n"; This method returns the name of the capture buffer referred to. In the case of a reference to a numbered capture (i.e. C returns false), this method returns C. =cut sub name { my ( $self ) = @_; return $self->{name}; } =head2 number print "The number is ", $ref->number(), "\n"; This method returns the number of the capture buffer referred to. In the case of a reference to a named capture (i.e. C returns true), this method returns C. =cut sub number { my ( $self ) = @_; return $self->{number}; } # Called by the lexer to record the capture number. sub __PPIX_LEXER__record_capture_number { my ( $self, $number ) = @_; if ( ! exists $self->{absolute} && exists $self->{number} && $self->{number} =~ m/ \A [-+] /smx ) { my $delta = $self->{number}; $delta > 0 and --$delta; # no -0 or +0. $self->{absolute} = $number + $delta; } return $number; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Structure.pm000444000765000024 2567314151156043 21306 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Structure - Represent structural elements. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(foo)}smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L. =head1 DESCRIPTION This class represents things that define the structure of the regular expression. This typically means brackets of various sorts, but to prevent proliferation of token classes the type of the regular expression is stored here. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Structure; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_QUANT COOKIE_REGEX_SET MINIMUM_PERL TOKEN_LITERAL @CARP_NOT }; # Tokens we are responsible for making, under at least some # circumstances. use PPIx::Regexp::Token::Comment (); use PPIx::Regexp::Token::Modifier (); use PPIx::Regexp::Token::Backreference (); use PPIx::Regexp::Token::Backtrack (); use PPIx::Regexp::Token::Recursion (); our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise my %quant = map { $_ => 1 } ')', ']'; sub can_be_quantified { my ( $self ) = @_; ref $self or return; return $quant{ $self->content() }; }; { my %explanation = ( '' => 'Match regexp', '(' => 'Capture or grouping', '(?[' => 'Extended character class', ')' => 'End capture or grouping', '[' => 'Character class', ']' => 'End character class', '])' => 'End extended character class', 'm' => 'Match regexp', 'qr' => 'Regexp object definition', 's' => 'Replace regexp with string or expression', '{' => 'Explicit quantifier', '}' => 'End explicit quantifier', ); sub __explanation { return \%explanation; } } sub is_quantifier { my ( $self ) = @_; ref $self or return; return $self->{is_quantifier}; } { # Note that the implementation equivocates on the ::Token::Structure # class, using it both for the initial token that determines the # type of the regex and things like parentheses internal to the # regex. Rather than sort out this equivocation, I have relied on # the currently-true assumption that 'qr' will not satisfy the # ::Token::Structure recognition logic, and the only way this class # can acquire this content is by the brute-force approach used to # generate the initial token object. my %perl_version_introduced = ( qr => '5.005', '(?[' => '5.017008', ); sub perl_version_introduced { my ( $self ) = @_; return $self->{perl_version_introduced} || $perl_version_introduced{ $self->content() } || MINIMUM_PERL; } } { my %delim = map { $_ => 1 } qw/ ( ) { } [ ] /; # Regular expressions to match various parenthesized tokens, and the # classes to make them into. my @paren_token = map { [ $_ => $_->__PPIX_TOKEN__recognize() ] } 'PPIx::Regexp::Token::Comment', 'PPIx::Regexp::Token::Modifier', 'PPIx::Regexp::Token::Backreference', 'PPIx::Regexp::Token::Backtrack', 'PPIx::Regexp::Token::Recursion', ; sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; # We are not interested in anything but delimiters. $delim{$character} or return; # Inside a character class, all the delimiters are normal characters # except for the close square bracket. if ( $tokenizer->cookie( COOKIE_CLASS ) ) { $character eq ']' or return $tokenizer->make_token( 1, TOKEN_LITERAL ); $tokenizer->cookie( COOKIE_CLASS, undef ); return 1; } # Open parentheses have various interesting possibilities ... if ( $character eq '(' ) { # Sometimes the whole bunch of parenthesized characters seems # naturally to be a token. foreach ( @paren_token ) { my ( $class, @recognize ) = @{ $_ }; foreach ( @recognize ) { my ( $regexp, $arg ) = @{ $_ }; my $accept = $tokenizer->find_regexp( $regexp ) or next; return $tokenizer->make_token( $accept, $class, $arg ); } } # Modifier changes are local to this parenthesis group $tokenizer->modifier_duplicate(); # The regex-set functionality introduced with 5.17.8 is most # conveniently handled by treating the initial '(?[' and # final '])' as ::Structure tokens. Fortunately for us, # perl5178delta documents that these may not have interior # spaces. if ( my $accept = $tokenizer->find_regexp( qr{ \A [(] [?] [[] }smx # ] ) - help for vim ) ) { $tokenizer->cookie( COOKIE_REGEX_SET, sub { return 1 } ); $tokenizer->modifier_modify( x => 1 ); # Implicitly /x return $accept; } # We expect certain tokens only after a left paren. $tokenizer->expect( 'PPIx::Regexp::Token::GroupType::Modifier', 'PPIx::Regexp::Token::GroupType::NamedCapture', 'PPIx::Regexp::Token::GroupType::Assertion', 'PPIx::Regexp::Token::GroupType::Code', 'PPIx::Regexp::Token::GroupType::BranchReset', 'PPIx::Regexp::Token::GroupType::Subexpression', 'PPIx::Regexp::Token::GroupType::Switch', 'PPIx::Regexp::Token::GroupType::Script_Run', 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run', ); # Accept the parenthesis. return 1; } # Close parentheses end modifier localization if ( $character eq ')' ) { $tokenizer->modifier_pop(); return 1; } # Open curlys are complicated because they may or may not represent # the beginning of a quantifier, depending on what comes before the # close curly. So we set a cookie to monitor the token stream for # interlopers. If all goes well, the right curly will find the # cookie and know it is supposed to be a quantifier. if ( $character eq '{' ) { # If the prior token can not be quantified, all this is # unnecessary. $tokenizer->prior_significant_token( 'can_be_quantified' ) or return 1; # We make our token now, before setting the cookie. Otherwise # the cookie has to deal with this token. my $token = $tokenizer->make_token( 1 ); # A cookie for the next '}'. my $commas = 0; my $allow_digit = 1; $tokenizer->cookie( COOKIE_QUANT, sub { my ( $tokenizer, $token ) = @_; $token or return 1; # Code for 5.33.6 and after. # We allow {,...}, and we allow space inside and # adjacent to the curlys, and around the comma if # any. But not interior to the numbers. if ( $token->isa( TOKEN_LITERAL ) ) { my $character = $token->content(); if ( $character =~ m/ \A \s \z /smx ) { # Digits only allowed if the prior # significant was an open curly or a comma. $allow_digit = $tokenizer->prior_significant_token( 'content' ) =~ m/ \A [{,] \z /smx; # } return 1; } $character eq ',' and return( ! $commas++ ); $allow_digit or return; return $character =~ m/ \A [0-9] \z /smx; } # Since we do not know what is in an interpolation, we # trustingly accept it. if ( $token->isa( 'PPIx::Regexp::Token::Interpolation' ) ) { return 1; } return; }, ); return $token; } # The close curly bracket is a little complicated because if the # cookie posted by the left curly bracket is still around, we are a # quantifier, otherwise not. if ( $character eq '}' ) { $tokenizer->cookie( COOKIE_QUANT, undef ) or return 1; $tokenizer->prior_significant_token( 'class' )->isa( __PACKAGE__ ) and return 1; my $token = $tokenizer->make_token( 1 ); $token->{is_quantifier} = 1; return $token; } # The parse rules are different inside a character class, so we set # another cookie. Sigh. If your tool is a hammer ... if ( $character eq '[' ) { # Set our cookie. Since it always returns 1, it does not matter # where in the following mess we set it. $tokenizer->cookie( COOKIE_CLASS, sub { return 1 } ); # Make our token now, since the easiest place to deal with the # beginning-of-character-class strangeness seems to be right # here. my @tokens = $tokenizer->make_token( 1 ); # Get the next character, returning tokens if there is none. defined ( $character = $tokenizer->peek() ) or return @tokens; # If we have a caret, it is a negation operator. Make its token # and fetch the next character, returning if none. if ( $character eq '^' ) { push @tokens, $tokenizer->make_token( 1, 'PPIx::Regexp::Token::Operator' ); defined ( $character = $tokenizer->peek() ) or return @tokens; } # If we have a close square at this point, it is not the end of # the class, but just a literal. Make its token. $character eq ']' and push @tokens, $tokenizer->make_token( 1, TOKEN_LITERAL ); # Return all tokens made. return @tokens; } # per perlop, the metas inside a [] are -]\^$. # per perlop, the metas outside a [] are {}[]()^$.|*+?\ # The difference is that {}[().|*+? are not metas in [], but - is. # Close bracket is complicated by the addition of regex sets. # And more complicated by the fact that you can have an # old-style character class inside a regex set. Fortunately they # have not (yet!) permitted nested regex sets. if ( $character eq ']' ) { # If we find '])' and COOKIE_REGEX_SET is present, we have a # regex set. We need to delete the cookie and accept both # characters. if ( ( my $accept = $tokenizer->find_regexp( # help vim - ( [ qr{ \A []] [)] }smx ) ) && $tokenizer->cookie( COOKIE_REGEX_SET ) ) { $tokenizer->cookie( COOKIE_REGEX_SET, undef ); return $accept; } # Otherwise we assume we're in a bracketed character class, # delete the cookie, and accept the close bracket. $tokenizer->cookie( COOKIE_CLASS, undef ); return 1; } return 1; } } # Called by the lexer once it has done its worst to all the tokens. # Called as a method with no arguments. The return is the number of # parse failures discovered when finalizing. sub __PPIX_LEXER__finalize { my ( $self ) = @_; delete $self->{is_quantifier}; return 0; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Unknown.pm000444000765000024 670214151156043 20715 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Unknown - Represent an unknown token =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'xyzzy' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This token represents something that could not be identified by the tokenizer. Sometimes the lexer can fix these up, but the presence of one of these in a finished parse represents something in the regular expression that was not understood. =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::Unknown; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub __new { my ( $class, $content, %arg ) = @_; defined $arg{error} or confess 'Programming error - error argument required'; my $self = $class->SUPER::__new( $content, %arg ) or return; $self->{error} = $arg{error}; $self->{explanation} = defined $arg{explanation} ? $arg{explanation} : $arg{error}; return $self; } # Return true if the token can be quantified, and false otherwise sub can_be_quantified { return }; sub explain { my ( $self ) = @_; return $self->{explanation}; } =head2 is_matcher This method returns C because, since we could not identify the token, we have no idea whether it matches anything. =cut sub is_matcher { return undef; } ## no critic (ProhibitExplicitReturnUndef) =head2 ordinal This method returns the results of the ord built-in on the content (meaning, of course, the first character of the content). No attempt is made to interpret the content, since after all this B the unknown token. =cut sub ordinal { my ( $self ) = @_; return ord $self->content(); } sub __PPIX_ELEM__rebless { my ( $class, $self, %arg ) = @_; my $rslt = $class->SUPER::__PPIX_ELEM__rebless( $self, %arg ); unless ( defined( $self->{error} = $arg{error} ) ) { Carp::cluck( 'Making unknown token with no error message' ); $self->{error} = 'Unspecified error'; $rslt++; } $self->{explanation} = defined $arg{explanation} ? $arg{explanation} : $arg{error}; return $rslt; } # Since the lexer does not count these on the way in (because it needs # the liberty to rebless them into a known class if it figures out what # is going on) we count them as failures at the finalization step. sub __PPIX_LEXER__finalize { return 1; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Unmatched.pm000444000765000024 354214151156043 21165 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Unmatched - Represent an unmatched right bracket =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class is used to represent an unmatched right bracket of any sort - parenthesis, square bracket, curly bracket, or whatever. This class is not generated by the tokenizer; instead the lexer reblesses a L into it when it is found to be unmatched. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Unmatched; use strict; use warnings; use base qw{ PPIx::Regexp::Token }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { return 'Unmatched token'; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/Whitespace.pm000444000765000024 571114151156043 21351 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::Whitespace - Represent whitespace =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{ foo }smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents whitespace. It will appear inside the regular expression only if the C modifier is present, but it may also appear between the type and the opening delimiter (e.g. C) or after the regular expression in a bracketed substitution (e.g. C). If the C modifier is present, it can also appear inside bracketed character classes. This was introduced in Perl 5.25.9. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::Whitespace; use strict; use warnings; use base qw{ PPIx::Regexp::Token::NoOp }; use PPIx::Regexp::Constant qw{ COOKIE_REGEX_SET MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; sub __new { my ( $class, $content, %arg ) = @_; defined $arg{perl_version_introduced} or $arg{perl_version_introduced} = ( grep { 127 < ord } split qr{}, $content ) ? '5.021001' : MINIMUM_PERL; return $class->SUPER::__new( $content, %arg ); } sub explain { my ( $self ) = @_; my $parent; if ( $parent = $self->parent() and $parent->isa( 'PPIx::Regexp' ) ) { return $self->SUPER::explain(); } elsif ( $self->in_regex_set() ) { return q; } elsif ( my $count = $self->modifier_asserted( 'x*' ) ) { return q . ( 'x' x $count ); } else { return $self->SUPER::explain(); } } sub whitespace { return 1; } # Objects of this class are generated either by the tokenizer itself # (when scanning for delimiters) or by PPIx::Regexp::Token::Literal (if # it hits a match for \s and finds the regular expression has the /x # modifier asserted. # # sub __PPIX_TOKENIZER__regexp { # my ( $class, $tokenizer, $character ) = @_; # # return scalar $tokenizer->find_regexp( qr{ \A \s+ }smx ); # # } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/CharClass000755000765000024 014151156043 20421 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/CharClass/POSIX.pm000444000765000024 1173214151156043 22042 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::CharClass::POSIX - Represent a POSIX character class =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{ [[:alpha:]] }smx' ) ->print(); =head1 INHERITANCE C is a L. C is the parent of L. =head1 DESCRIPTION This class represents a POSIX character class. It will only be recognized within a character class. Note that collating symbols (e.g. C<[.ch.]>) and equivalence classes (e.g. C<[=a=]>) are valid in the POSIX standard, but are not valid in Perl regular expressions. These end up being represented by L, and are considered a parse failure. =head1 METHODS This class provides the following public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::CharClass::POSIX; use strict; use warnings; use base qw{ PPIx::Regexp::Token::CharClass }; use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_REGEX_SET MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; ##=head2 is_case_sensitive ## ##This override of the superclass method of the same name returns true if ##the character class is C<[:lower:]> or C<[:upper:]>, and false (but ##defined) for all other POSIX character classes. ## ##=cut ## ##{ ## my %case_sensitive = map { $_ => 1 } qw{ [:lower:] [:upper:] }; ## ## sub is_case_sensitive { ## my ( $self ) = @_; ## return $case_sensitive{ $self->content() } || 0; ## } ##} sub perl_version_introduced { # my ( $self ) = @_; return '5.006'; } { my %explanation = ( '[:alnum:]' => 'Any alphanumeric character', '[:^alnum:]' => 'Anything but an alphanumeric character', '[:alpha:]' => 'Any alphabetic', '[:^alpha:]' => 'Anything but an alphabetic', '[:ascii:]' => 'Any character in the ASCII character set', '[:^ascii:]' => 'Anything but a character in the ASCII character set', '[:blank:]' => 'A GNU extension, equal to a space or a horizontal tab ("\\t")', '[:^blank:]' => 'A GNU extension, anything but a space or a horizontal tab ("\\t")', '[:cntrl:]' => 'Any control character', '[:^cntrl:]' => 'Anything but a control character', '[:digit:]' => 'Any decimal digit', '[:^digit:]' => 'Anything but a decimal digit', '[:graph:]' => 'Any non-space printable character', '[:^graph:]' => 'Anything but a non-space printable character', '[:lower:]' => 'Any lowercase character', '[:^lower:]' => 'Anything but a lowercase character', '[:print:]' => 'Any printable character', '[:^print:]' => 'Anything but a printable character', '[:punct:]' => 'Any graphical character excluding "word" characters', '[:^punct:]' => 'Anything but a graphical character excluding "word" characters', '[:space:]' => 'Any whitespace character', '[:^space:]' => 'Anything but a whitespace character', '[:upper:]' => 'Any uppercase character', '[:^upper:]' => 'Anything but an uppercase character', '[:word:]' => 'A Perl extension, equivalent to "\\w"', '[:^word:]' => 'A Perl extension, equivalent to "\\W"', '[:xdigit:]' => 'Any hexadecimal digit', '[:^xdigit:]' => 'Anything but a hexadecimal digit', ); sub __explanation { return \%explanation; } sub __no_explanation { my ( $self ) = @_; local $_ = $self->content(); m/ \A \[ = ( . ) = \] \z /smx and return "POSIX Character Equivalent (to '$1'; " . "unimplemented in Perl)"; return q; } } { my %class = ( ':' => __PACKAGE__, ); sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer ) = @_; # Invocant, $character unused $tokenizer->cookie( COOKIE_CLASS ) or $tokenizer->cookie( COOKIE_REGEX_SET ) or return; if ( my $accept = $tokenizer->find_regexp( qr{ \A [[] ( [.=:] ) \^? .*? \1 []] }smx ) ) { my ( $punc ) = $tokenizer->capture(); return $tokenizer->make_token( $accept, $class{$punc} || __PACKAGE__ . '::Unknown' ); } return; } } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/CharClass/Simple.pm000444000765000024 2156414151156043 22375 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::CharClass::Simple - This class represents a simple character class =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{\w}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents one of the simple character classes that can occur anywhere in a regular expression. This includes not only the truly simple things like \w, but also Unicode properties, including properties with wildcard values. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::CharClass::Simple; use strict; use warnings; use base qw{ PPIx::Regexp::Token::CharClass }; use PPIx::Regexp::Constant qw{ COOKIE_CLASS LITERAL_LEFT_CURLY_REMOVED_PHASE_1 LITERAL_LEFT_CURLY_REMOVED_PHASE_2 MINIMUM_PERL TOKEN_LITERAL TOKEN_UNKNOWN @CARP_NOT }; our $VERSION = '0.082'; use constant UNICODE_PROPERTY_LITERAL_VALUE => qr/ \{ \s* \^? \w [\w:=\s-]* \} | [CLMNPSZ] # perluniprops for 5.26.1 /smx; use constant UNICODE_PROPERTY_LITERAL => qr/ \A \\ [Pp] (?: @{[ UNICODE_PROPERTY_LITERAL_VALUE ]} ) /smx; # CAVEAT: The following regular expression, despite its name, matches # ALL unicode property values. To actually match a wildcard property you # must first eliminate anything that matches UNICODE_PROPERTY_LITERAL or # UNICODE_PROPERTY_NAME_MATCH use constant UNICODE_PROPERTY_WILDCARD => qr/ \A \\ [Pp] \{ \s* [\w\s-]+ [:=] [^}]+ \} /smx; use constant UNICODE_PROPERTY_NAME_MATCH => qr< \A \\ [Pp] \{ \s* na (?: me? )? [:=] / [^/]+ / \} >smx; use constant UNICODE_PROPERTY => qr/ @{[ UNICODE_PROPERTY_LITERAL ]} | @{[ UNICODE_PROPERTY_NAME_MATCH ]} | @{[ UNICODE_PROPERTY_WILDCARD ]} /smx; { my %kind_of_match = ( p => 'with', P => 'without', ); my %explanation = ( '.' => 'Match any character', '\\C' => 'Match a single octet (removed in 5.23.0)', '\\D' => 'Match any character but a decimal digit', '\\H' => 'Match a non-horizontal-white-space character', '\\N' => 'Match any character but a new-line character', '\\R' => 'Match a generic new-line character', '\\S' => 'Match non-white-space character', '\\V' => 'Match a non-vertical-white-space character', '\\W' => 'Match non-word character', '\\X' => 'Match a Unicode extended grapheme cluster', '\\d' => 'Match decimal digit', '\\h' => 'Match a horizontal-white-space character', '\\s' => 'Match white-space character', '\\v' => 'Match a vertical-white-space character', '\\w' => 'Match word character', ); sub __explanation { return \%explanation; } sub explain { my ( $self ) = @_; if ( $self->content() =~ m/ \A \\ ( [Pp] ) ( [{] .* [}] | . ) \z /smx ) { my ( $kind, $prop ) = ( $1, $2 ); my $literal = ( $prop =~ m/ \A @{[ UNICODE_PROPERTY_LITERAL_VALUE ]} \z /smx ); if ( 1 < length $prop ) { $prop =~ s/ \A [{] //smx; $prop =~ s/ [}] \z //smx; } $literal and return sprintf q, $kind_of_match{$kind}, $prop; return sprintf q, $kind_of_match{$kind}, $prop; } return $self->SUPER::explain(); } } ##=head2 is_case_sensitive ## ##This override of the superclass method returns true for Unicode ##properties that specify case, and false (but defined) for all ##other character classes. ## ##The classes that specify case are documented in ##L. ## ##B This method returns false (but defined) for user-defined ##Unicode properties. It should return C. This bug B be fixed ##if I find a way to identify all system-defined Unicode properties. ## ##=cut ## ##sub is_case_sensitive { ## my ( $self ) = @_; ## exists $self->{is_case_sensitive} ## and return $self->{is_case_sensitive}; ## return ( $self->{is_case_sensitive} = $self->_is_case_sensitive() ); ##} ##{ ## my %case_sensitive = map { $_ => 1 } qw{ ## generalcategory=lowercaseletter generalcategory=ll ## gc=lowercaseletter gc=ll ## generalcategory=titlecaseletter generalcategory=lt ## gc=titlecaseletter gc=lt ## generalcategory=uppercaseletter generalcategory=lu ## gc=uppercaseletter gc=lu ## lowercaseletter lowercase lower ll ## titlecaseletter titlecase title lt ## uppercaseletter uppercase upper lu ## lowercase=y lower=y lowercase=n lower=n ## titlecase=y title=y titlecase=n title=n ## uppercase=y upper=y uppercase=n upper=n ## }; ## ## sub _is_case_sensitive { ## my ( $self ) = @_; ## my $content = $self->content(); ## $content =~ m/ \A \\ p [{] ( .* ) [}] /smxi ## or return 0; ## $content = lc $1; ## $content =~ s/ \A ^ //smx; ## $content =~ s/ [\s_-] //smxg; ## $content =~ s/ \A is //smx; ## $content =~ s/ : /=/smxg; ## $content =~ s/ = (?: yes | t | true ) \b /=y/smxg; ## $content =~ s/ = (?: no | f | false ) \b /=n/smxg; ## return $case_sensitive{$content} || 0; ## } ## ##} { my %introduced = ( '\\h' => '5.009005', # Before this, parsed as 'h' '\\v' => '5.009005', # Before this, parsed as 'v' '\\H' => '5.009005', # Before this, parsed as 'H' '\\N' => '5.011', # Before this, an error. '\\V' => '5.009005', # Before this, parsed as 'V' '\\R' => '5.009005', '\\C' => '5.006', '\\X' => '5.006', ); sub perl_version_introduced { my ( $self ) = @_; my $content = $self->content(); if ( defined( my $minver = $introduced{$content} ) ) { return $minver; } # I must have read perl5113delta and thought this # represented the change they were talking about, but I sure # don't see it now. So, until things become clearer ... # $content =~ m/ \G .*? [\s=-] /smxgc # and return '5.011003'; $content =~ UNICODE_PROPERTY_LITERAL and return '5.006001'; $content =~ UNICODE_PROPERTY_NAME_MATCH and return '5.031010'; $content =~ UNICODE_PROPERTY_WILDCARD and return '5.029009'; return MINIMUM_PERL; } } { my %removed = ( '\\C' => '5.023', # Before this, matched an octet ); sub perl_version_removed { my ( $self ) = @_; return $removed{ $self->content() }; } } # This is one of the larger complications of # https://rt.perl.org/Public/Bug/Display.html?id=128213 # where it transpired that un-escaped literal left curlies were not # giving warnings/errors in /.{/, /\p{...}{/, and /\P{...}{/, but were # for all the others that bin into this class (e.g. /\s{/). # Note that the perldelta for 5.25.1 and 5.26.0 do not acknowledge tha # phased deprecation, and pretend that everything was done on the phase # 1 schedule. This appears to be deliberate per # https://rt.perl.org/Ticket/Display.html?id=131352 sub __following_literal_left_curly_disallowed_in { my ( $self ) = @_; q<.> eq ( my $content = $self->content() ) and return LITERAL_LEFT_CURLY_REMOVED_PHASE_2; $content =~ m/ \A \\ p \{ /smxi and return LITERAL_LEFT_CURLY_REMOVED_PHASE_2; return LITERAL_LEFT_CURLY_REMOVED_PHASE_1; } sub __PPIX_TOKENIZER__regexp { my ( undef, $tokenizer, $character ) = @_; my $in_class = $tokenizer->cookie( COOKIE_CLASS ); if ( $character eq '.' ) { $in_class and return $tokenizer->make_token( 1, TOKEN_LITERAL ); return 1; } if ( my $accept = $tokenizer->find_regexp( qr{ \A \\ [wWsSdDvVhHXRNC] }smx ) ) { if ( $in_class ) { my $match = $tokenizer->match(); # As of Perl 5.11.5, [\N] is a fatal error. '\\N' eq $match and return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => '\\N invalid inside character class', }, ); # \R is not recognized inside a character class. It # eventually ends up as a literal. '\\R' eq $match and return; } return $accept; } if ( my $accept = $tokenizer->find_regexp( UNICODE_PROPERTY ) ) { return $accept; } if ( my $accept = $tokenizer->find_regexp( qr< \A \\ p [{] \s* [}] >smx ) ) { return $tokenizer->make_token( $accept, TOKEN_UNKNOWN, { error => 'Empty \\p{} is an error', }, ); } return; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/CharClass/POSIX000755000765000024 014151156043 21323 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/CharClass/POSIX/Unknown.pm000444000765000024 451214151156043 23457 0ustar00tomstaff000000000000package PPIx::Regexp::Token::CharClass::POSIX::Unknown; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Token::CharClass::POSIX }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; sub perl_version_introduced { # my ( $self ) = @_; return MINIMUM_PERL; } # Note that these guys are recognized by PPIx::Regexp::CharClass::POSIX, # and if one of them becomes supported that is where the change needs to # be made. # This is the handiest way to make this object represent a parse error. sub __PPIX_LEXER__finalize { return 1; } 1; __END__ =head1 NAME PPIx::Regexp::Token::CharClass::POSIX::Unknown - Represent an unknown or unsupported POSIX character class =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{ [[=a=]] }smx' ) -print() =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents POSIX character classes which are recognized but not supported by Perl. At the moment this means C<[=a=]> (equivalence classes), and C<[.ch.]> (collating symbols). B If any of these becomes supported by Perl in the future, they will become represented as L objects, with an appropriate C value. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2010-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType000755000765000024 014151156043 20514 5ustar00tomstaff000000000000PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Assertion.pm000444000765000024 756514151156043 23173 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::Assertion - Represent a look ahead or look behind assertion =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo(?=bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the parenthesized look ahead and look behind assertions. =head1 METHODS This class provides the following public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::GroupType::Assertion; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ COOKIE_LOOKAROUND_ASSERTION @CARP_NOT }; our $VERSION = '0.082'; use constant EXPL_NLA => 'Negative look-ahead assertion'; use constant EXPL_NLB => 'Negative look-behind assertion'; use constant EXPL_PLA => 'Positive look-ahead assertion'; use constant EXPL_PLB => 'Positive look-behind assertion'; use constant DEF => { '?!' => { expl => EXPL_NLA, look_ahead => 1, }, '*nla:' => { expl => EXPL_NLA, intro => '5.027009', look_ahead => 1, }, '*negative_lookahead:' => { expl => EXPL_NLA, intro => '5.027009', look_ahead => 1, }, '? { expl => EXPL_NLB, intro => '5.005', }, '*nlb:' => { expl => EXPL_NLB, intro => '5.027009', }, '*negative_lookbehind:' => { expl => EXPL_NLB, intro => '5.027009', }, '?=' => { expl => EXPL_PLA, look_ahead => 1, positive => 1, }, '*pla:' => { expl => EXPL_PLA, intro => '5.027009', look_ahead => 1, positive => 1, }, '*positive_lookahead:' => { expl => EXPL_PLA, intro => '5.027009', look_ahead => 1, positive => 1, }, '?<=' => { expl => EXPL_PLB, intro => '5.005', positive => 1, }, '*plb:' => { expl => EXPL_PLB, intro => '5.027009', positive => 1, }, '*positive_lookbehind:' => { expl => EXPL_PLB, intro => '5.027009', positive => 1, }, }; __PACKAGE__->__setup_class(); sub __match_setup { my ( undef, $tokenizer ) = @_; # $class not used $tokenizer->__cookie_exists( COOKIE_LOOKAROUND_ASSERTION ) and return; my $nest_depth = 1; $tokenizer->cookie( COOKIE_LOOKAROUND_ASSERTION, sub { my ( undef, $token ) = @_; # $tokenizer not used $token and $token->isa( 'PPIx::Regexp::Token::Structure' ) and $nest_depth += ( { '(' => 1, ')' => -1, }->{ $token->content() } || 0 ); return $nest_depth; }, ); return; } =head2 is_look_ahead This method returns a true value if the assertion is a look-ahead assertion, or a false value if it is a look-behind assertion. =cut sub is_look_ahead { my ( $self ) = @_; return $self->DEF->{ $self->unescaped_content() }->{look_ahead}; } =head2 is_positive This method returns a true value if the assertion is a positive assertion, or a false value if it is a negative assertion. =cut sub is_positive { my ( $self ) = @_; return $self->DEF->{ $self->unescaped_content() }->{positive}; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Atomic_Script_Run.pm000444000765000024 406714151156043 24602 0ustar00tomstaff000000000000package PPIx::Regexp::Token::GroupType::Atomic_Script_Run; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; use constant EXPL => 'All characters must be in same script, atomic version'; use constant DEF => { '*atomic_script_run:' => { expl => EXPL, intro => '5.027009', }, '*asr:' => { expl => EXPL, intro => '5.027009', }, }; __PACKAGE__->__setup_class(); 1; __END__ =head1 NAME PPIx::Regexp::Token::GroupType::Atomic_Script_Run - Represent an atomic script run specifier =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(*atomic_script_run:\d)}' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This token represents the specifier for an atomic script run - namely the C<'*atomic_script_run:'> or C<'*asr:'> that comes after the left parenthesis. This is new with Perl 5.27.9. If this construction does not make it into Perl 5.28, this class will be retracted. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2018-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/BranchReset.pm000444000765000024 414314151156043 23411 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::BranchReset - Represent a branch reset specifier =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?|(foo)|(bar))}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This token represents the specifier for a branch reset - namely the C that comes after the left parenthesis. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::GroupType::BranchReset; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; =begin comment # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; { my %explanation = ( '?|' => 'Re-use capture group numbers', ); sub __explanation { return \%explanation; } } sub perl_version_introduced { return '5.009005'; } sub __defining_string { return '?|'; } =end comment =cut use constant DEF => { '?|' => { expl => 'Re-use capture group numbers', intro => '5.009005', }, }; __PACKAGE__->__setup_class(); 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Code.pm000444000765000024 444214151156043 22065 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::Code - Represent one of the embedded code indicators =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?{print "hello world!\n")}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This method represents one of the embedded code indicators, either '?' or '??', in the zero-width assertion (?{ print "Hello, world!\n" }) or the old-style deferred expression syntax my $foo; $foo = qr{ foo (??{ $foo }) }smx; =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::GroupType::Code; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub __match_setup { my ( undef, $tokenizer ) = @_; # Invocant unused $tokenizer->expect( qw{ PPIx::Regexp::Token::Code } ); return; } use constant DEF => { '??' => { expl => 'Evaluate code, use as regexp at this point', intro => '5.006', }, '?p' => { expl => 'Evaluate code, use as regexp at this point (removed in 5.9.5)', intro => '5.005', # Presumed. I can find no documentation. remov => '5.009005', }, '?' => { expl => 'Evaluate code. Always matches.', intro => '5.005', }, }; __PACKAGE__->__setup_class( { suffix => '{', }, ); 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Modifier.pm000444000765000024 632114151156043 22747 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::Modifier - Represent the modifiers in a modifier group. =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?i:foo)}smx' ) ->print(); =head1 INHERITANCE C is a L and a L. C has no descendants. =head1 DESCRIPTION This class represents the modifiers in a modifier group. The useful functionality comes from L. =head1 METHODS This class provides no public methods beyond those provided by its superclasses. =cut package PPIx::Regexp::Token::GroupType::Modifier; use strict; use warnings; use base qw{ PPIx::Regexp::Token::Modifier PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ MINIMUM_PERL @CARP_NOT }; our $VERSION = '0.082'; { my %perl_version_introduced = ( '?:' => MINIMUM_PERL, ); sub perl_version_introduced { my ( $self ) = @_; my $content = $self->unescaped_content(); exists $perl_version_introduced{$content} and return $perl_version_introduced{$content}; my $ver = $self->SUPER::perl_version_introduced(); $ver > 5.005 and return $ver; return '5.005'; } } =begin comment # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub __PPIX_TOKENIZER__regexp { my ( $class, $tokenizer, $character, $char_type ) = @_; # Note that the optional escapes are because any of the # non-open-bracket punctuation characters might be our delimiter. my $accept; $accept = $tokenizer->find_regexp( qr{ \A \\? [?] [[:lower:]]* \\? -? [[:lower:]]* \\? : }smx ) and return $accept; $accept = $tokenizer->find_regexp( qr{ \A \\? [?] \^ [[:lower:]]* \\? : }smx ) and return $accept; return; } =end comment =cut sub __make_group_type_matcher { return { '' => [ qr{ \A [?] [[:lower:]]* -? [[:lower:]]* : }smx, qr{ \A [?] \^ [[:lower:]]* : }smx, ], '?' => [ qr{ \A \\ [?] [[:lower:]]* -? [[:lower:]]* : }smx, qr{ \A \\ [?] \^ [[:lower:]]* : }smx, ], '-' => [ qr{ \A [?] [[:lower:]]* (?: \\ - )? [[:lower:]]* : }smx, qr{ \A [?] \^ [[:lower:]]* : }smx, ], ':' => [ qr{ \A [?] [[:lower:]]* -? [[:lower:]]* \\ : }smx, qr{ \A [?] \^ [[:lower:]]* \\ : }smx, ], }; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/NamedCapture.pm000444000765000024 572514151156043 23570 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::NamedCapture - Represent a named capture =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?foo)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents a named capture specification. Its content will be something like one of the following: ? ?'NAME' ?P =head1 METHODS This class provides the following public methods. Methods not documented here are private, and unsupported in the sense that the author reserves the right to change or remove them without notice. =cut package PPIx::Regexp::Token::GroupType::NamedCapture; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use Carp qw{ confess }; use PPIx::Regexp::Constant qw{ RE_CAPTURE_NAME @CARP_NOT }; our $VERSION = '0.082'; use constant TOKENIZER_ARGUMENT_REQUIRED => 1; sub __new { my ( $class, $content, %arg ) = @_; defined $arg{perl_version_introduced} or $arg{perl_version_introduced} = '5.009005'; my $self = $class->SUPER::__new( $content, %arg ); foreach my $name ( $arg{tokenizer}->capture() ) { defined $name or next; $self->{name} = $name; return $self; } confess 'Programming error - can not figure out capture name'; } # Return true if the token can be quantified, and false otherwise # sub can_be_quantified { return }; sub explain { my ( $self ) = @_; return sprintf q, $self->name(); } =head2 name This method returns the name of the capture. =cut sub name { my ( $self ) = @_; return $self->{name}; } sub __make_group_type_matcher { return { '' => [ qr/ \A [?] P? < ( @{[ RE_CAPTURE_NAME ]} ) > /smxo, qr/ \A [?] ' ( @{[ RE_CAPTURE_NAME ]} ) ' /smxo, ], '?' => [ qr/ \A \\ [?] P? < ( @{[ RE_CAPTURE_NAME ]} ) > /smxo, qr/ \A \\ [?] ' ( @{[ RE_CAPTURE_NAME ]} ) ' /smxo, ], q{'} => [ qr/ \A [?] P? < ( @{[ RE_CAPTURE_NAME ]} ) > /smxo, qr/ \A [?] \\ ' ( @{[ RE_CAPTURE_NAME ]} ) \\ ' /smxo, ], }; } 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Script_Run.pm000444000765000024 427014151156043 23302 0ustar00tomstaff000000000000package PPIx::Regexp::Token::GroupType::Script_Run; use 5.006; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; use constant EXPL => 'All characters must be in same script'; use constant DEF => { '+script_run:' => { expl => EXPL, intro => '5.027008', remov => '5.027009', }, '*script_run:' => { expl => EXPL, intro => '5.027009', }, '*sr:' => { expl => EXPL, intro => '5.027009', }, }; __PACKAGE__->__setup_class(); 1; __END__ =head1 NAME PPIx::Regexp::Token::GroupType::Script_Run - Represent a script run specifier =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(*script_run:\d)}' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This token represents the specifier for a script run - namely the C<'+script_run:'>, C<'*script_run:'>, or C<'*sr:'> that comes after the left parenthesis. The first form was added in Perl 5.27.8 but retracted in favor of the second ant third forms (which are equivalent) in Perl 5.27.9. If this construction does not make it into Perl 5.28, this class will be retracted. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2018-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Subexpression.pm000444000765000024 353214151156043 24063 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::Subexpression - Represent an independent subexpression marker =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{foo(?>bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the '?>' after a left parenthesis that identifies an independent subexpression. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::GroupType::Subexpression; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; use constant EXPL => 'Match subexpression without backtracking'; use constant DEF => { '?>' => { expl => EXPL, intro => '5.005', }, '*atomic:' => { expl => EXPL, intro =>'5.027009', }, }; __PACKAGE__->__setup_class(); 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/lib/PPIx/Regexp/Token/GroupType/Switch.pm000444000765000024 376214151156043 22460 0ustar00tomstaff000000000000=head1 NAME PPIx::Regexp::Token::GroupType::Switch - Represent the introducing characters for a switch =head1 SYNOPSIS use PPIx::Regexp::Dumper; PPIx::Regexp::Dumper->new( 'qr{(?(1)foo|bar)}smx' ) ->print(); =head1 INHERITANCE C is a L. C has no descendants. =head1 DESCRIPTION This class represents the characters right after a left parenthesis that introduce a switch. Strictly speaking these characters are '?(', but this class only takes the '?'. =head1 METHODS This class provides no public methods beyond those provided by its superclass. =cut package PPIx::Regexp::Token::GroupType::Switch; use strict; use warnings; use base qw{ PPIx::Regexp::Token::GroupType }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; our $VERSION = '0.082'; sub __match_setup { my ( undef, $tokenizer ) = @_; # Invocant unused $tokenizer->expect( qw{ PPIx::Regexp::Token::Condition } ); return; } use constant DEF => { '?' => { expl => q, intro => '5.005', }, }; __PACKAGE__->__setup_class( { suffix => '(', }, ); 1; __END__ =head1 SUPPORT Support is by the author. Please file bug reports at L, L, or in electronic mail to the author. =head1 AUTHOR Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2009-2021 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set textwidth=72 : PPIx-Regexp-0.082/t000755000765000024 014151156043 13121 5ustar00tomstaff000000000000PPIx-Regexp-0.082/t/basic.t000444000765000024 3126514151156043 14553 0ustar00tomstaff000000000000package main; use strict; use warnings; use lib qw{ inc }; use Test::More 0.88; require_ok( 'My::Module::Mock_Tokenizer' ) or BAIL_OUT; require_ok( 'PPI::Document' ) or BAIL_OUT( q{PPI::Document is a prerequisite. Without it, we're hosed.} ); require_ok( 'PPIx::Regexp::Util' ) or BAIL_OUT; can_ok( 'PPIx::Regexp::Util', 'is_ppi_regexp_element' ) or BAIL_OUT; require_ok( 'PPIx::Regexp' ) or BAIL_OUT; class_isa_ok( 'PPIx::Regexp', 'PPIx::Regexp::Node' ) or BAIL_OUT; require_ok( 'PPIx::Regexp::Constant' ); class_isa_ok( 'PPIx::Regexp::Constant', 'Exporter' ); require_ok( 'PPIx::Regexp::Dumper' ) or BAIL_OUT; class_isa_ok( 'PPIx::Regexp::Dumper', 'PPIx::Regexp::Support' ); isa_ok( PPIx::Regexp::Dumper->new( 'xyzzy' ), 'PPIx::Regexp::Dumper' ); require_ok( 'PPIx::Regexp::Element' ); require_ok( 'PPIx::Regexp::Lexer' ); class_isa_ok( 'PPIx::Regexp::Lexer', 'PPIx::Regexp::Support' ); require_ok( 'PPIx::Regexp::Node' ); class_isa_ok( 'PPIx::Regexp::Node', 'PPIx::Regexp::Element' ); require_ok( 'PPIx::Regexp::Node::Range' ); class_isa_ok( 'PPIx::Regexp::Node::Range', 'PPIx::Regexp::Node' ); require_ok( 'PPIx::Regexp::Node::Unknown' ); class_isa_ok( 'PPIx::Regexp::Node::Unknown', 'PPIx::Regexp::Node' ); require_ok( 'PPIx::Regexp::Structure' ); class_isa_ok( 'PPIx::Regexp::Structure', 'PPIx::Regexp::Node' ); require_ok( 'PPIx::Regexp::Structure::Assertion' ); class_isa_ok( 'PPIx::Regexp::Structure::Assertion', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Atomic_Script_Run' ); class_isa_ok( 'PPIx::Regexp::Structure::Atomic_Script_Run', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::BranchReset' ); class_isa_ok( 'PPIx::Regexp::Structure::BranchReset', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Capture' ); class_isa_ok( 'PPIx::Regexp::Structure::Capture', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::CharClass' ); class_isa_ok( 'PPIx::Regexp::Structure::CharClass', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Code' ); class_isa_ok( 'PPIx::Regexp::Structure::Code', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Main' ); class_isa_ok( 'PPIx::Regexp::Structure::Main', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Modifier' ); class_isa_ok( 'PPIx::Regexp::Structure::Modifier', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::NamedCapture' ); class_isa_ok( 'PPIx::Regexp::Structure::NamedCapture', 'PPIx::Regexp::Structure::Capture' ); require_ok( 'PPIx::Regexp::Structure::Quantifier' ); class_isa_ok( 'PPIx::Regexp::Structure::Quantifier', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Regexp' ); class_isa_ok( 'PPIx::Regexp::Structure::Regexp', 'PPIx::Regexp::Structure::Main' ); require_ok( 'PPIx::Regexp::Structure::Replacement' ); class_isa_ok( 'PPIx::Regexp::Structure::Replacement', 'PPIx::Regexp::Structure::Main' ); require_ok( 'PPIx::Regexp::Structure::Script_Run' ); class_isa_ok( 'PPIx::Regexp::Structure::Script_Run', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Subexpression' ); class_isa_ok( 'PPIx::Regexp::Structure::Subexpression', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Switch' ); class_isa_ok( 'PPIx::Regexp::Structure::Switch', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Structure::Unknown' ); class_isa_ok( 'PPIx::Regexp::Structure::Unknown', 'PPIx::Regexp::Structure' ); require_ok( 'PPIx::Regexp::Support' ); require_ok( 'PPIx::Regexp::Token' ); class_isa_ok( 'PPIx::Regexp::Token', 'PPIx::Regexp::Element' ); isa_ok( PPIx::Regexp::Token->__new( 'xyzzy' ), 'PPIx::Regexp::Token' ); require_ok( 'PPIx::Regexp::Token::Assertion' ); class_isa_ok( 'PPIx::Regexp::Token::Assertion', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Assertion->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Assertion' ); require_ok( 'PPIx::Regexp::Token::Backreference' ); class_isa_ok( 'PPIx::Regexp::Token::Backreference', 'PPIx::Regexp::Token::Reference' ); isa_ok( PPIx::Regexp::Token::Backreference->__new( '\\1' ), 'PPIx::Regexp::Token::Backreference' ); require_ok( 'PPIx::Regexp::Token::Backtrack' ); class_isa_ok( 'PPIx::Regexp::Token::Backtrack', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Backtrack->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Backtrack' ); require_ok( 'PPIx::Regexp::Token::CharClass' ); class_isa_ok( 'PPIx::Regexp::Token::CharClass', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::CharClass->__new( 'xyzzy' ), 'PPIx::Regexp::Token::CharClass' ); require_ok( 'PPIx::Regexp::Token::CharClass::POSIX' ); class_isa_ok( 'PPIx::Regexp::Token::CharClass::POSIX', 'PPIx::Regexp::Token::CharClass' ); isa_ok( PPIx::Regexp::Token::CharClass::POSIX->__new( 'xyzzy' ), 'PPIx::Regexp::Token::CharClass::POSIX' ); require_ok( 'PPIx::Regexp::Token::CharClass::Simple' ); class_isa_ok( 'PPIx::Regexp::Token::CharClass::Simple', 'PPIx::Regexp::Token::CharClass' ); isa_ok( PPIx::Regexp::Token::CharClass::Simple->__new( 'xyzzy' ), 'PPIx::Regexp::Token::CharClass::Simple' ); require_ok( 'PPIx::Regexp::Token::Code' ); class_isa_ok( 'PPIx::Regexp::Token::Code', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Code->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new(), ), 'PPIx::Regexp::Token::Code' ); require_ok( 'PPIx::Regexp::Token::Comment' ); class_isa_ok( 'PPIx::Regexp::Token::Comment', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Comment->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Comment' ); require_ok( 'PPIx::Regexp::Token::Condition' ); class_isa_ok( 'PPIx::Regexp::Token::Condition', 'PPIx::Regexp::Token::Reference' ); isa_ok( PPIx::Regexp::Token::Condition->__new( '(1)' ), 'PPIx::Regexp::Token::Condition' ); require_ok( 'PPIx::Regexp::Token::Control' ); class_isa_ok( 'PPIx::Regexp::Token::Control', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Control->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Control' ); require_ok( 'PPIx::Regexp::Token::Delimiter' ); class_isa_ok( 'PPIx::Regexp::Token::Delimiter', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Delimiter->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Delimiter' ); require_ok( 'PPIx::Regexp::Token::Greediness' ); class_isa_ok( 'PPIx::Regexp::Token::Greediness', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Greediness->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Greediness' ); require_ok( 'PPIx::Regexp::Token::GroupType' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::GroupType->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType' ); require_ok( 'PPIx::Regexp::Token::GroupType::Assertion' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Assertion', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::Assertion->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType::Assertion' ); require_ok( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::Atomic_Script_Run->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run' ); require_ok( 'PPIx::Regexp::Token::GroupType::BranchReset' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::BranchReset', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::BranchReset->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType::BranchReset' ); require_ok( 'PPIx::Regexp::Token::GroupType::Code' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Code', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::Code->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new(), ), 'PPIx::Regexp::Token::GroupType::Code' ); require_ok( 'PPIx::Regexp::Token::GroupType::Modifier' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Modifier', 'PPIx::Regexp::Token::GroupType' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Modifier', 'PPIx::Regexp::Token::Modifier' ); isa_ok( PPIx::Regexp::Token::GroupType::Modifier->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new(), ), 'PPIx::Regexp::Token::GroupType::Modifier' ); require_ok( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::NamedCapture', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::NamedCapture->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new( capture => [ 'foo' ], ), ), 'PPIx::Regexp::Token::GroupType::NamedCapture' ); require_ok( 'PPIx::Regexp::Token::GroupType::Subexpression' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Subexpression', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::Subexpression->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType::Subexpression' ); require_ok( 'PPIx::Regexp::Token::GroupType::Script_Run' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Script_Run', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::Script_Run->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType::Script_Run' ); require_ok( 'PPIx::Regexp::Token::GroupType::Switch' ); class_isa_ok( 'PPIx::Regexp::Token::GroupType::Switch', 'PPIx::Regexp::Token::GroupType' ); isa_ok( PPIx::Regexp::Token::GroupType::Switch->__new( 'xyzzy' ), 'PPIx::Regexp::Token::GroupType::Switch' ); require_ok( 'PPIx::Regexp::Token::Interpolation' ); class_isa_ok( 'PPIx::Regexp::Token::Interpolation', 'PPIx::Regexp::Token::Code' ); isa_ok( PPIx::Regexp::Token::Interpolation->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new(), ), 'PPIx::Regexp::Token::Interpolation' ); require_ok( 'PPIx::Regexp::Token::Literal' ); class_isa_ok( 'PPIx::Regexp::Token::Literal', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Literal->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Literal' ); require_ok( 'PPIx::Regexp::Token::Modifier' ); class_isa_ok( 'PPIx::Regexp::Token::Modifier', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Modifier->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new(), ), 'PPIx::Regexp::Token::Modifier' ); require_ok( 'PPIx::Regexp::Token::NoOp' ); class_isa_ok( 'PPIx::Regexp::Token::NoOp', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::NoOp->__new( 'xyzzy' ), 'PPIx::Regexp::Token::NoOp' ); require_ok( 'PPIx::Regexp::Token::Operator' ); class_isa_ok( 'PPIx::Regexp::Token::Operator', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Operator->__new( 'xyzzy', tokenizer => My::Module::Mock_Tokenizer->new(), ), 'PPIx::Regexp::Token::Operator' ); require_ok( 'PPIx::Regexp::Token::Quantifier' ); class_isa_ok( 'PPIx::Regexp::Token::Quantifier', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Quantifier->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Quantifier' ); require_ok( 'PPIx::Regexp::Token::Recursion' ); class_isa_ok( 'PPIx::Regexp::Token::Recursion', 'PPIx::Regexp::Token::Reference' ); isa_ok( PPIx::Regexp::Token::Recursion->__new( '(?1)' ), 'PPIx::Regexp::Token::Recursion' ); require_ok( 'PPIx::Regexp::Token::Reference' ); class_isa_ok( 'PPIx::Regexp::Token::Reference', 'PPIx::Regexp::Token' ); # This is an abstract class and should never be instantiated in the # first place. # isa_ok( PPIx::Regexp::Token::Reference->__new( 'xyzzy' ), # 'PPIx::Regexp::Token::Reference' ); require_ok( 'PPIx::Regexp::Token::Structure' ); class_isa_ok( 'PPIx::Regexp::Token::Structure', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Structure->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Structure' ); require_ok( 'PPIx::Regexp::Token::Unknown' ); class_isa_ok( 'PPIx::Regexp::Token::Unknown', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Unknown->__new( 'xyzzy', error => 'bogus' ), 'PPIx::Regexp::Token::Unknown' ); require_ok( 'PPIx::Regexp::Token::Unmatched' ); class_isa_ok( 'PPIx::Regexp::Token::Unmatched', 'PPIx::Regexp::Token' ); isa_ok( PPIx::Regexp::Token::Unmatched->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Unmatched' ); require_ok( 'PPIx::Regexp::Token::Whitespace' ); class_isa_ok( 'PPIx::Regexp::Token::Whitespace', 'PPIx::Regexp::Token::NoOp' ); isa_ok( PPIx::Regexp::Token::Whitespace->__new( 'xyzzy' ), 'PPIx::Regexp::Token::Whitespace' ); require_ok( 'PPIx::Regexp::Tokenizer' ); class_isa_ok( 'PPIx::Regexp::Tokenizer', 'PPIx::Regexp::Support' ); isa_ok( PPIx::Regexp::Tokenizer->new( 'xyzzy' ), 'PPIx::Regexp::Tokenizer' ); done_testing; sub class_isa_ok { my ( $class, $isa ) = @_; @_ = ( eval { $class->isa( $isa ) }, "$class isa $isa", ); goto &ok, } 1; __END__ # ex: set textwidth=72 : PPIx-Regexp-0.082/t/fuzz.t000444000765000024 126014151156043 14440 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use lib qw{ inc }; use My::Module::Test; use My::Module::Test qw{ __quote }; note <<'EOD'; Obviously this is not a true fuzz test, just a collection of pathological strings discovered via fuzz testing. Because the parse of an invalid string may change, we just see if the code survived the test. EOD survival( 'x//' ); survival( ' ' ); done_testing; sub survival { my ( $expr ) = @_; my $title = join ' ', 'Parse', __quote( $expr ); eval { PPIx::Regexp->new( $expr ); 1; } and do { @_ = ( $title ); goto &pass; } or do { @_ = ( "$title failed: $@" ); goto &fail; }; } 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/t/locations.t000444000765000024 1156614151156043 15467 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use PPI::Document; use PPIx::Regexp; use Test::More 0.88; # Because of done_testing(); { note 'Parse s/\\n foo\\n/bar/smxg'; my $doc = PPI::Document->new( \<<'EOD' ); #line 42 the_answer s/ foo /bar/smxg; EOD my $subs = $doc->find( 'PPI::Token::Regexp::Substitute' ); ok $subs, 'Found PPI::Token::Regexp::Substitute'; cmp_ok @{ $subs }, '==', 1, 'Found exactly one PPI::Token::Regexp::Substitute'; my $re = PPIx::Regexp->new( $subs->[0] ); my @token = $re->tokens(); cmp_ok scalar @token, '==', 13, 'Found 13 tokens in regex'; is_deeply $token[0]->location(), [ 2, 1, 1, 42, 'the_answer' ], q; cmp_ok $token[0]->line_number(), '==', 2, q; cmp_ok $token[0]->column_number(), '==', 1, q; cmp_ok $token[0]->visual_column_number(), '==', 1, q; cmp_ok $token[0]->logical_line_number(), '==', 42, q; cmp_ok $token[0]->logical_filename(), 'eq', 'the_answer', q; is_deeply $token[1]->location(), [ 2, 2, 2, 42, 'the_answer' ], q; is_deeply $token[2]->location(), [ 2, 3, 3, 42, 'the_answer' ], q; is_deeply $token[3]->location(), [ 3, 5, 5, 43, 'the_answer' ], q; is_deeply $token[4]->location(), [ 3, 6, 6, 43, 'the_answer' ], q; is_deeply $token[5]->location(), [ 3, 7, 7, 43, 'the_answer' ], q; is_deeply $token[6]->location(), [ 3, 8, 8, 43, 'the_answer' ], q; is_deeply $token[7]->location(), [ 4, 1, 1, 44, 'the_answer' ], q; is_deeply $token[8]->location(), [ 4, 2, 2, 44, 'the_answer' ], q; is_deeply $token[9]->location(), [ 4, 3, 3, 44, 'the_answer' ], q; is_deeply $token[10]->location(), [ 4, 4, 4, 44, 'the_answer' ], q; is_deeply $token[11]->location(), [ 4, 5, 5, 44, 'the_answer' ], q; is_deeply $token[12]->location(), [ 4, 6, 6, 44, 'the_answer' ], q; is_deeply $re->location(), [ 2, 1, 1, 42, 'the_answer' ], q; } { note 'Parse s/([[:alpha:]]+)/ reverse $1 /smxge'; my $doc = PPI::Document->new( \<<'EOD' ); #line 86 "get_smart" s/([[:alpha:]]+)/ reverse $1 /smxge; EOD my $subs = $doc->find( 'PPI::Token::Regexp::Substitute' ); ok $subs, 'Found PPI::Token::Regexp::Substitute'; cmp_ok @{ $subs }, '==', 1, 'Found exactly one PPI::Token::Regexp::Substitute'; my $re = PPIx::Regexp->new( $subs->[0] ); my @token = $re->tokens(); cmp_ok scalar @token, '==', 12, 'Found 12 tokens in regex'; is_deeply $token[0]->location(), [ 2, 1, 1, 86, 'get_smart' ], q; is_deeply $token[1]->location(), [ 2, 2, 2, 86, 'get_smart' ], q; is_deeply $token[2]->location(), [ 2, 3, 3, 86, 'get_smart' ], q; is_deeply $token[3]->location(), [ 2, 4, 4, 86, 'get_smart' ], q; is_deeply $token[4]->location(), [ 2, 5, 5, 86, 'get_smart' ], q; is_deeply $token[5]->location(), [ 2, 14, 14, 86, 'get_smart' ], q; is_deeply $token[6]->location(), [ 2, 15, 15, 86, 'get_smart' ], q; is_deeply $token[7]->location(), [ 2, 16, 16, 86, 'get_smart' ], q; is_deeply $token[8]->location(), [ 2, 17, 17, 86, 'get_smart' ], q; is_deeply $token[9]->location(), [ 2, 18, 18, 86, 'get_smart' ], q; is_deeply $token[10]->location(), [ 2, 30, 30, 86, 'get_smart' ], q; is_deeply $token[11]->location(), [ 2, 31, 31, 86, 'get_smart' ], q; note q; my $code = $token[9]->ppi(); @token = $code->tokens(); cmp_ok scalar @token, '==', 5, 'Found 5 PPI tokens in replacement expression'; is_deeply $token[0]->location(), [ 2, 1, 1, 86, 'get_smart' ], q; is_deeply $token[1]->location(), [ 2, 19, 19, 86, 'get_smart' ], q; note <<'EOD'; The above is not the same as token 9 of the RE because of the leading white space in the expression. EOD is_deeply $token[2]->location(), [ 2, 26, 26, 86, 'get_smart' ], q; is_deeply $token[3]->location(), [ 2, 27, 27, 86, 'get_smart' ], q; is_deeply $token[4]->location(), [ 2, 29, 29, 86, 'get_smart' ], q; } done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/t/parse.t000444000765000024 124077414151156043 14654 0ustar00tomstaff000000000000package main; use strict; use warnings; use PPIx::Regexp::Constant qw{ SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS }; use Test::More 0.88; use lib qw{ inc }; use My::Module::Test; # NOTE we use this circumlocution to hide the :encoding() from # xt/author/minimum_perl.t and Perl::MinimumVersion. The two-argument # binmode itself is OK under Perl 5.6 but the :encoding() is not. But if # we're 5.6 then SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS is false, # so the binmode() never gets executed. use constant OUTPUT_ENCODING => ':encoding(utf-8)'; if ( SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS ) { my $builder = My::Module::Test->builder(); foreach my $method ( qw{ output failure_output todo_output } ) { my $handle = $builder->$method(); binmode $handle, OUTPUT_ENCODING; } } use charnames qw{ :full }; # NOTE: It is intended that all individual tests in this file be # generated by PPIx::Regexp::Dumper. Special tests should go in # t/unit.t. tokenize( '' ); count ( 0 ); parse ( '' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 0 ); tokenize( 'fubar' ); count ( 1 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( 'fubar' ); parse ( 'fubar' ); value ( failures => [], 1); klass ( 'PPIx::Regexp' ); count ( 1 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( 'fubar' ); tokenize( 'qrxxx' ); count ( 1 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( 'qrxxx' ); error ( 'Tokenizer found illegal first characters' ); parse ( 'qrxxx' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 1 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( 'qrxxx' ); error ( 'Tokenizer found illegal first characters' ); tokenize( 'qr xxx' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( 'x' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( 'x' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( 'qr xxx' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 0 ); choose ( child => 2, start => [] ); count ( 1 ); choose ( child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( 'x' ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( 'x' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( '/(/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(/' ); value ( failures => [], 1); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 0 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, finish => 0 ); klass ( undef ); content ( undef ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/)/' ); value ( failures => [], 1); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unmatched' ); content ( ')' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm//' ); count ( 4 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm//' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 0 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr\'\'' ); count ( 4 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr\'\'' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 0 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr /foo/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr /foo/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm{}' ); count ( 4 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm{}' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 0 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm{}smx' ); count ( 4 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'm{}smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 0 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( 'm{foo}' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm{foo}' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/m{foo\\t\\0334\\o{61}\\cK\\xBB\\_\\{\\}\\!/' ); count ( 19 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'm' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\t' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\033' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 4 ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\o{61}' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\cK' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\xBB' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\_' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\{' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\}' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\!' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 18 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/m{foo\\t\\0334\\o{61}\\cK\\xBB\\_\\{\\}\\!/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 15 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'm' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\t' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\033' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 4 ); choose ( child => 1, child => 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\o{61}' ); choose ( child => 1, child => 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\cK' ); choose ( child => 1, child => 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\xBB' ); choose ( child => 1, child => 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\_' ); choose ( child => 1, child => 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\{' ); choose ( child => 1, child => 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\}' ); choose ( child => 1, child => 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\!' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\\\{/' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\\\{/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\N{LATIN SMALL LETTER A}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\N{LATIN SMALL LETTER A}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\N{LATIN SMALL LETTER A}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\N{LATIN SMALL LETTER A}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\C{4}/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\C' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 4 ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\C{4}/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\C' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 1 ); choose ( child => 1, child => 1, start => [] ); count ( 1 ); choose ( child => 1, child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 1, type => [] ); count ( 0 ); choose ( child => 1, child => 1, finish => [] ); count ( 1 ); choose ( child => 1, child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 4 ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(\\C)/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\C' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(\\C)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\C' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr{foo+}' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr{foo+}' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr{foo?}' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr{foo?}' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr{foo*}' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr{foo*}' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr{foo*bar+}' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr{foo*bar+}' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 8 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr{+}' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '+' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr{+}' ); value ( failures => [], 1); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '+' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?<=f+)/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 2 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<=' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '+' ); error ( 'Lookbehind longer than 255 not implemented' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr/foo{3}/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '3' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr/foo{3}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 1 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr/foo{3,}/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '3' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr/foo{3,}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 2 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr/foo{3,5}/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '3' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '5' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr/foo{3,5}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 5 ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr/foo{3,$bar}/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$bar' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr/foo{3,$bar}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$bar' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr/foo{,3}/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '3' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # This parse is as of Perl 5.33.6. Previously this expression did not # contain a quantifier. parse ( 'qr/foo{,3}/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 2 ); choose ( child => 1, child => 3, start => [] ); count ( 1 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 3, type => [] ); count ( 0 ); choose ( child => 1, child => 3, finish => [] ); count ( 1 ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/{}/' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/{}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?<=f{2,})/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 2 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<=' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Structure::Unknown' ); count ( 2 ); choose ( child => 1, child => 0, child => 1, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 0, child => 1, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 1, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 0, child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 2 ); choose ( child => 1, child => 0, child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/x{}/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/x{}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/{2}/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '2' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/{2}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 2 ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/f{oo/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/f{oo/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(f{oo)/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(f{oo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 4 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/{?+}/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '+' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/{?+}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/./' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/./' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\\\d{3,5}+.*?/' ); count ( 15 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'd' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '3' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '5' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '?' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\\\d{3,5}+.*?/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'd' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 3 ); choose ( child => 1, child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 1, child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 5 ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '?' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/\\w+\\W*\\s?\\S\\d\\D\\v\\V\\h\\H/' ); count ( 17 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\W' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\s' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\S' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\d' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\D' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\v' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\V' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\h' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\H' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/\\w+\\W*\\s?\\S\\d\\D\\v\\V\\h\\H/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 13 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\W' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\s' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\S' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\d' ); choose ( child => 1, child => 8 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\D' ); choose ( child => 1, child => 9 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\v' ); choose ( child => 1, child => 10 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\V' ); choose ( child => 1, child => 11 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\h' ); choose ( child => 1, child => 12 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\H' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/\\p{Punctuation}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\p{Punctuation}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/\\p{Punctuation}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\p{Punctuation}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/\\p{My::InEvenChars}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\p{My::InEvenChars}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/\\p{My::InEvenChars}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\p{My::InEvenChars}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/foo bar/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/foo bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/foo bar/x' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( 'm/foo bar/x' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( '/$foo/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$foo/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/${foo}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '${foo}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/${foo}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '${foo}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm\'$foo\'' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm\'$foo\'' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\'' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/A $foo{bar} baz/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo{bar}' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/A $foo{bar} baz/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo{bar}' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/A $foo{3} baz/' ); count ( 14 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '3' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/A $foo{3} baz/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 8 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 1 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/A @{[ scalar time ]} baz/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '@{[ scalar time ]}' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/A @{[ scalar time ]} baz/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '@{[ scalar time ]}' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/[$foo]/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/[$foo]/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 's/([\\x00-\\x1f])/$UNPRINTABLE[ord($1)]/g' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\x00' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\x1f' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$UNPRINTABLE[ord($1)]' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'g' ); parse ( 's/([\\x00-\\x1f])/$UNPRINTABLE[ord($1)]/g' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Node::Range' ); count ( 3 ); choose ( child => 1, child => 0, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\x00' ); choose ( child => 1, child => 0, child => 0, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 0, child => 0, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\x1f' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 1 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$UNPRINTABLE[ord($1)]' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'g' ); tokenize( 's/$$foo{bar}/$$bar{foo}/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$$foo{bar}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$$bar{foo}' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 's/$$foo{bar}/$$bar{foo}/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$$foo{bar}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 1 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$$bar{foo}' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/a|b/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/a|b/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/$|$($)@+@-/' ); count ( 14 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '@' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '@' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/$|$($)@+@-/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 8 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '@' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '@' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/$-$+$$$[$]$-[0]$+[0]$-{foo}$+{foo}/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$-' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$+' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$$' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$[' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$]' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$-[0]' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$+[0]' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$-{foo}' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$+{foo}' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$-$+$$$[$]$-[0]$+[0]$-{foo}$+{foo}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 9 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$-' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$+' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$$' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$[' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$]' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$-[0]' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$+[0]' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$-{foo}' ); choose ( child => 1, child => 8 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$+{foo}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/\\Q\\w$foo\\\\E\\E/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\Q' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\w' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'E' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/\\Q\\w$foo\\\\E\\E/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 6 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\Q' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\w' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'E' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\lX\\uy\\LFOO\\Ubar\\Q.^\\E/' ); count ( 20 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\l' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'X' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\u' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'y' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\L' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'F' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'O' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'O' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\U' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\Q' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '.' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '^' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( 18 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 19 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\lX\\uy\\LFOO\\Ubar\\Q.^\\E/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 16 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\l' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'X' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\u' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'y' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\L' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'F' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'O' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'O' ); choose ( child => 1, child => 8 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\U' ); choose ( child => 1, child => 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 1, child => 12 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\Q' ); choose ( child => 1, child => 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '.' ); choose ( child => 1, child => 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '^' ); choose ( child => 1, child => 15 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/$foo?/' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$foo?/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/$#foo/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$#foo' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$#foo/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$#foo' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/$#$foo{3}/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$#$foo' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$#$foo{3}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$#$foo' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 1 ); choose ( child => 1, child => 1, start => [] ); count ( 1 ); choose ( child => 1, child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 1, type => [] ); count ( 0 ); choose ( child => 1, child => 1, finish => [] ); count ( 1 ); choose ( child => 1, child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/$#{^_foo}{4}/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$#{^_foo}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 4 ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$#{^_foo}{4}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$#{^_foo}' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 1 ); choose ( child => 1, child => 1, start => [] ); count ( 1 ); choose ( child => 1, child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 1, type => [] ); count ( 0 ); choose ( child => 1, child => 1, finish => [] ); count ( 1 ); choose ( child => 1, child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 4 ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo\\Ub\\wr\\Ebaz/' ); count ( 15 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\U' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo\\Ub\\wr\\Ebaz/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 11 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\U' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( child => 1, child => 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\Fu/' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\F' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'u' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\Fu/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\F' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'u' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/ foo (?# match foo )+/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '(?# match foo )' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/ foo (?# match foo )+/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '(?# match foo )' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/ foo # match foo /smx' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '# match foo ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'm/ foo # match foo /smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 5 ); choose ( child => 1, start => [] ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '# match foo ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( '/^#/x' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '^' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( '/^#/x' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '^' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( 'm/[# ]/x' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '#' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( 'm/[# ]/x' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 2 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '#' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( '/(?x)\\1?\\g{-1}*\\k{1,3}+/' ); count ( 19 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{-1}' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 18 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?x)\\1?\\g{-1}*\\k{1,3}+/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 8 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{-1}' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 3 ); choose ( child => 1, child => 6, start => [] ); count ( 1 ); choose ( child => 1, child => 6, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 6, type => [] ); count ( 0 ); choose ( child => 1, child => 6, finish => [] ); count ( 1 ); choose ( child => 1, child => 6, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 6, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( child => 1, child => 6, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 1, child => 6, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?x)\\1?\\g{-1}*\\k{1,3}+/' ); count ( 19 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{-1}' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 18 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?x)\\1?\\g{-1}*\\k{1,3}+/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 8 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{-1}' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Structure::Quantifier' ); count ( 3 ); choose ( child => 1, child => 6, start => [] ); count ( 1 ); choose ( child => 1, child => 6, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( child => 1, child => 6, type => [] ); count ( 0 ); choose ( child => 1, child => 6, finish => [] ); count ( 1 ); choose ( child => 1, child => 6, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( child => 1, child => 6, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( child => 1, child => 6, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ',' ); choose ( child => 1, child => 6, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 3 ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # In the replacement string only \n is a backreference tokenize( 's/(x)/\\g{1}\\1/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\g' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 's/(x)/\\g{1}\\1/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 5 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\g' ); choose ( child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( child => 2, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2, child => 4 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 's/(foo)/\\1bar/g' ); count ( 14 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'g' ); parse ( 's/(foo)/\\1bar/g' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 4 ); choose ( child => 2, start => 0 ); klass ( undef ); content ( undef ); choose ( child => 2, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); choose ( child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 2, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'g' ); tokenize( 's/x/$1/e' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '$1' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'e' ); parse ( 's/x/$1/e' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 1 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '$1' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'e' ); tokenize( 's/x/$1/eeg' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '$1' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'eeg' ); parse ( 's/x/$1/eeg' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '$1' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'eeg' ); false ( asserts => [ 'e' ] ); true ( asserts => [ 'ee' ] ); true ( asserts => [ 'g' ] ); tokenize( '/^\\A\\b\\B\\G\\Z\\z$/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '^' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\A' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\B' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\G' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\Z' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\z' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/^\\A\\b\\B\\G\\Z\\z$/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 8 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '^' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\A' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\B' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\G' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\Z' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\z' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/(?smx)bar/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?smx)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/(?smx)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?smx)' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/(?smx:foo)bar/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?smx:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/(?smx:foo)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?smx:' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?-x:#1)#2/x' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?-x:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '#' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#2' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( '/(?-x:#1)#2/x' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 2 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?-x:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '#' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#2' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( '/(?-x)#1(?x)#2/x' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?-x)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '#' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?x)' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#2' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( '/(?-x)#1(?x)#2/x' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 5 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?-x)' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '#' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 1 ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?x)' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#2' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( '/(?$foo:$bar)/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ':' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$bar' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?$foo:$bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 3 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?' ); choose ( child => 1, child => 0, type => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 1, child => 0, type => 2 ); klass ( 'PPIx::Regexp::Token::GroupType' ); content ( ':' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$bar' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?$foo)$bar/' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$bar' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?$foo)$bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 0 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 2 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?' ); choose ( child => 1, child => 0, type => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$bar' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?$on-$off:$foo)/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$on' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$off' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ':' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?$on-$off:$foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 5 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?' ); choose ( child => 1, child => 0, type => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$on' ); choose ( child => 1, child => 0, type => 2 ); klass ( 'PPIx::Regexp::Token::GroupType' ); content ( '-' ); choose ( child => 1, child => 0, type => 3 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$off' ); choose ( child => 1, child => 0, type => 4 ); klass ( 'PPIx::Regexp::Token::GroupType' ); content ( ':' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?$on-$off)$foo/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$on' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$off' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?$on-$off)$foo/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 0 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 4 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?' ); choose ( child => 1, child => 0, type => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$on' ); choose ( child => 1, child => 0, type => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( child => 1, child => 0, type => 3 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$off' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$foo' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?bar)/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?\'foo\'bar)/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?\'foo\'' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?\'foo\'bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?\'foo\'' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?Pbar)/' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?P' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?Pbar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?P' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr/(?\\w)/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr/(?\\w)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(?=bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?=' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(?=bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?=' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(*pla:bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*pla:' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(*pla:bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*pla:' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(*positive_lookahead:bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*positive_lookahead:' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(*positive_lookahead:bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*positive_lookahead:' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(?!bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?!' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(?!bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?!' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(*nla:bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*nla:' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(*nla:bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*nla:' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(*negative_lookahead:bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*negative_lookahead:' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(*negative_lookahead:bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*negative_lookahead:' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?<=foo)bar/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<=' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?<=foo)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<=' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*plb:foo)bar/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*plb:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*plb:foo)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*plb:' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*positive_lookbehind:foo)bar/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*positive_lookbehind:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*positive_lookbehind:foo)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*positive_lookbehind:' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(? [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '? 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*nlb:foo)bar/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*nlb:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*nlb:foo)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*nlb:' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*negative_lookbehind:foo)bar/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*negative_lookbehind:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*negative_lookbehind:foo)bar/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '*negative_lookbehind:' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*PRUNE:foo)x/' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Backtrack' ); content ( '(*PRUNE:foo)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*PRUNE:foo)x/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Backtrack' ); content ( '(*PRUNE:foo)' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/[^]0-9\\w]/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '^' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ']' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '0' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '9' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'm/[^]0-9\\w]/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '^' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ']' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Node::Range' ); count ( 3 ); choose ( child => 1, child => 0, child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 0 ); choose ( child => 1, child => 0, child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 0, child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 9 ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'm/ [\\w\\{] /smx' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'm/ [\\w\\{] /smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 2 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\{' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( 'm/ [\\w{] /smx' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'm/ [\\w{] /smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 2 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( 'm/ [-\\\\^\\$\\]] /smx' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '^' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\$' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\]' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'm/ [-\\\\^\\$\\]] /smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 5 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '-' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '^' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\$' ); choose ( child => 1, child => 0, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\]' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( 'm/ [\\\\\\^^.\\||()\\[] /smx' ); count ( 17 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\^' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '^' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '.' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\|' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '|' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '(' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ')' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\[' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'm/ [\\\\\\^^.\\||()\\[] /smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'm' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 9 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\^' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '^' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '.' ); choose ( child => 1, child => 0, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\|' ); choose ( child => 1, child => 0, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '|' ); choose ( child => 1, child => 0, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '(' ); choose ( child => 1, child => 0, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ')' ); choose ( child => 1, child => 0, child => 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\[' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( '/[[:upper:]]+[[:^lower:]]?/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:upper:]' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:^lower:]' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/[[:upper:]]+[[:^lower:]]?/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:upper:]' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 2, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:^lower:]' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 'qr{ \\A \\\\ x \\{ \\w+ \\} }smx' ); count ( 18 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\A' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\{' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\}' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); parse ( 'qr{ \\A \\\\ x \\{ \\w+ \\} }smx' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 13 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\A' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\\\' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\{' ); choose ( child => 1, child => 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 8 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 1, child => 9 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '+' ); choose ( child => 1, child => 10 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\}' ); choose ( child => 1, child => 12 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); tokenize( 's/foo\\Kbar/baz/' ); count ( 15 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\K' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 's/foo\\Kbar/baz/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 7 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\K' ); choose ( child => 1, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 3 ); choose ( child => 2, start => 0 ); klass ( undef ); content ( undef ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 's/$CR?$LF/\\n/g' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$CR' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$LF' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\n' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'g' ); parse ( 's/$CR?$LF/\\n/g' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$CR' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$LF' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 1 ); choose ( child => 2, start => 0 ); klass ( undef ); content ( undef ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\n' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'g' ); tokenize( '/$RE{delimited}{-delim=>\'"\'}{-esc=>\'\\\\\'}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$RE{delimited}{-delim=>\'"\'}{-esc=>\'\\\\\'}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/$RE{delimited}{-delim=>\'"\'}{-esc=>\'\\\\\'}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$RE{delimited}{-delim=>\'"\'}{-esc=>\'\\\\\'}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 's%(.*?${/})%%o' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '%' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '?' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '${/}' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '%' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '%' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'o' ); parse ( 's%(.*?${/})%%o' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '%' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '%' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 4 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '*' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '?' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '${/}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 0 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '%' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'o' ); tokenize( 's[foo] [bar]' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '[' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( ']' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '[' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( ']' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 's[foo] [bar]' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 5 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '[' ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( ']' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 3 ); choose ( child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '[' ); choose ( child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( ']' ); choose ( child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?{ print "Hello, world!\\n" })/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{ print "Hello, world!\\n" }' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?{ print "Hello, world!\\n" })/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Code' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{ print "Hello, world!\\n" }' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(??{ $foo })/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '??' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{ $foo }' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(??{ $foo })/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Code' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '??' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{ $foo }' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?|(foo))/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::BranchReset' ); content ( '?|' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?|(foo))/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::BranchReset' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::BranchReset' ); content ( '?|' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 3 ); choose ( child => 1, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?1)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?1)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?1)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?1)' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?+2)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?+2)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?+2)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?+2)' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?-3)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?-3)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?-3)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?-3)' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?R)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?R)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?R)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?R)' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?&foo)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?&foo)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?&foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?&foo)' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?P>foo)/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?P>foo)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?P>foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?P>foo)' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/foo(?>bar)/' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::GroupType::Subexpression' ); content ( '?>' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/foo(?>bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Structure::Subexpression' ); count ( 3 ); choose ( child => 1, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 3, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Subexpression' ); content ( '?>' ); choose ( child => 1, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 3, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 3, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(})/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '}' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/]/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/]/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ']' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(])/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(])/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ']' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?(1)foo)/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?(1)foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?(1)foo|bar)/' ); count ( 15 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?(1)foo|bar)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 8 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 4 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 0, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?(1)foo|bar|baz)/' ); count ( 19 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 18 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?(1)foo|bar|baz)/' ); value ( failures => [], 1); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 12 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 4 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 0, child => 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 1, child => 0, child => 8 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '|' ); choose ( child => 1, child => 0, child => 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?()foo)/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '()' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?()foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '()' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?(\'baz\')foo)/' ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(\'baz\')' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?(\'baz\')foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(\'baz\')' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?(?{0})foo)/' ); count ( 14 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{0}' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?(?{0})foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure::Code' ); count ( 1 ); choose ( child => 1, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?' ); choose ( child => 1, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{0}' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # TODO /(?(R\d*)..)/, /(?(R&name)...)/, /(?(DEFINE)...)/. tokenize( '/ ( ?<= bar ) /x' ); count ( 15 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<=' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); parse ( '/ ( ?<= bar ) /x' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 4 ); choose ( child => 1, child => 0, start => [] ); count ( 2 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, type => [] ); count ( 2 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<=' ); choose ( child => 1, child => 0, type => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); tokenize( 'qr!(?<\\!foo)bar!' ); count ( 13 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '!' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<\\!' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '!' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 'qr!(?<\\!foo)bar!' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '!' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '!' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Assertion' ); count ( 3 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Assertion' ); content ( '?<\\!' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'r' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?{ foo } )/' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{ foo }' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?{ foo } )/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Code' ); count ( 2 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '{ foo }' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 's/\\b/\\b/' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\b' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); finis (); parse ( 's/\\b/\\b/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 1 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\b' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( 's\\/$\\\\' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\\' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '/' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\\' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\\' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 's\\/$\\\\' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\\' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\\' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '/' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 0 ); choose ( child => 2, start => [] ); count ( 0 ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '\\' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/ . /' ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/ . /' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/ . /', default_modifiers => [ 'smx' ] ); count ( 7 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/ . /', default_modifiers => [ 'smx' ] ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '.' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?[ \\P{Foo} & ( [:alpha:] | [ . \\d ] - [9] ) ])/' ); count ( 33 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(?[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\P{Foo}' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '&' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:alpha:]' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 14 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 15 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 16 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '.' ); choose ( 17 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 18 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\d' ); choose ( 19 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 20 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 21 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 22 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( 23 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 24 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 25 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 9 ); choose ( 26 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 27 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 28 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 29 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 30 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '])' ); choose ( 31 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 32 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?[ \\P{Foo} & ( [:alpha:] | [ . \\d ] - [9] ) ])/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::RegexSet' ); count ( 6 ); choose ( child => 1, child => 0, start => [] ); count ( 2 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(?[' ); choose ( child => 1, child => 0, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '])' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\P{Foo}' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '&' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4 ); klass ( 'PPIx::Regexp::Structure' ); count ( 10 ); choose ( child => 1, child => 0, child => 4, start => [] ); count ( 2 ); choose ( child => 1, child => 0, child => 4, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 4, start => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 4, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 4, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 4, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:alpha:]' ); choose ( child => 1, child => 0, child => 4, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 2 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 0, child => 4, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 4 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 5 ); choose ( child => 1, child => 0, child => 4, child => 4, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 4, child => 4, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, child => 4, child => 4, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 4, child => 4, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 4, child => 4, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 4, child => 4, child => 0 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 4, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '.' ); choose ( child => 1, child => 0, child => 4, child => 4, child => 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 4, child => 3 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\d' ); choose ( child => 1, child => 0, child => 4, child => 4, child => 4 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 5 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 6 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 0, child => 4, child => 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4, child => 8 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, child => 4, child => 8, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 4, child => 8, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, child => 4, child => 8, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 4, child => 8, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 4, child => 8, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 4, child => 8, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 9 ); choose ( child => 1, child => 0, child => 4, child => 9 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 5 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # The /n qualifier prevents parens from capturing. tokenize( '/(foo)/n' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'n' ); parse ( '/(foo)/n' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure' ); count ( 3 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'n' ); # The /n qualifier does not prevent named captures tokenize( '/(?foo)/n' ); count ( 10 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'n' ); parse ( '/(?foo)/n' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 3 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'n' ); note q; tokenize( '/(?x-i:f o o)/' ); count ( 12 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?x-i:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(?x-i:f o o)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 5 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?x-i:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\N{}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\N{}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\N{}/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\N{}' ); error ( 'Empty Unicode character name' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\N{}/', strict => 1 ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\N{}' ); error ( 'Empty Unicode character name' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\N{}/', strict => 1 ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\N{}' ); error ( 'Empty Unicode character name' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/[A-Z]/', strict => 1 ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'Z' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/[A-Z]/', strict => 1 ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Node::Range' ); count ( 3 ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( child => 1, child => 0, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 0, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'Z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/[A-z]/', strict => 1 ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/[A-z]/', strict => 1 ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Node::Unknown' ); count ( 3 ); error ( 'Non-portable range ends prohibited by "use re \'strict\'"' ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'A' ); choose ( child => 1, child => 0, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 0, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # Unterminated replacement tokenize( 's/foo/' ); count ( 1 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( 's/foo/' ); error ( 'Tokenizer found mismatched replacement delimiters' ); parse ( 's/foo/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 1 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( 's/foo/' ); error ( 'Tokenizer found mismatched replacement delimiters' ); # bracketed replacement with embedded Perl comment tokenize( 's{foo} #{bar} {baz}' ); count ( 14 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#{bar} ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 11 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( 13 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( 's{foo} #{bar} {baz}' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 6 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '#{bar} ' ); choose ( child => 4 ); klass ( 'PPIx::Regexp::Structure::Replacement' ); count ( 3 ); choose ( child => 4, start => [] ); count ( 1 ); choose ( child => 4, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '{' ); choose ( child => 4, type => [] ); count ( 0 ); choose ( child => 4, finish => [] ); count ( 1 ); choose ( child => 4, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '}' ); choose ( child => 4, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'b' ); choose ( child => 4, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 4, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/\\b{/' ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '{' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/\\b{/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '{' ); error ( 'Unterminated bound type' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/[ a]/xx' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'xx' ); parse ( '/[ a]/xx' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 2 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'xx' ); tokenize( '/(+script_run:\\w)/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Script_Run' ); content ( '+script_run:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(+script_run:\\w)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Script_Run' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Script_Run' ); content ( '+script_run:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*script_run:\\w)/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Script_Run' ); content ( '*script_run:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*script_run:\\w)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Script_Run' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Script_Run' ); content ( '*script_run:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*sr:\\w)/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Script_Run' ); content ( '*sr:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*sr:\\w)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Script_Run' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Script_Run' ); content ( '*sr:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*atomic_script_run:\\w)/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run' ); content ( '*atomic_script_run:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*atomic_script_run:\\w)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Atomic_Script_Run' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run' ); content ( '*atomic_script_run:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(*asr:\\w)/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run' ); content ( '*asr:' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( '/(*asr:\\w)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Atomic_Script_Run' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run' ); content ( '*asr:' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); tokenize( '/(?^)(\\d)/n' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?^)' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\d' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'n' ); parse ( '/(?^)(\\d)/n' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '(?^)' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 1, start => [] ); count ( 1 ); choose ( child => 1, child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 1, type => [] ); count ( 0 ); choose ( child => 1, child => 1, finish => [] ); count ( 1 ); choose ( child => 1, child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\d' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'n' ); SKIP: { SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS or skip 'Weird delimiters test requires Perl 5.8.3 or above', 43; my $delim = "\N{COMBINING CIRCUMFLEX ACCENT}"; tokenize( "qr ${delim}foo${delim}" ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); parse ( "qr ${delim}foo${delim}" ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 2, start => [] ); count ( 1 ); choose ( child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); } SKIP: { SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS or skip 'Truly weird delimiters test requires Perl 5.8.3 or above', 114; =begin comment $ENV{AUTHOR_TESTING} or skip 'Truly weird delimiters are noisy, therefore author tests', 114; =end comment =cut no warnings qw{ utf8 }; my $delim = "\N{U+FFFE}"; # Permanent noncharacter replace_characters( $delim => '\\N{U+FFFE}', ); tokenize( "qr ${delim}x$delim" ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); value ( perl_version_introduced => [], '5.005' ); value ( perl_version_removed => [], undef ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); value ( perl_version_introduced => [], '5.008003' ); value ( perl_version_removed => [], undef ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); value ( perl_version_introduced => [], '5.008003' ); value ( perl_version_removed => [], undef ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); parse ( "qr ${delim}x$delim" ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); value ( perl_version_introduced => [], '5.008003' ); value ( perl_version_removed => [], undef ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); value ( perl_version_introduced => [], '5.005' ); value ( perl_version_removed => [], undef ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 2, start => [] ); count ( 1 ); choose ( child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); '5.18' le $] or $ENV{AUTHOR_TESTING} or skip 'Illegal characters are noisy below Perl 5.18, and therefore author tests', 57; $delim = "\N{U+11FFFF}"; # Illegal character replace_characters( $delim => '\\N{U+11FFFF}', ); tokenize( "qr ${delim}x$delim" ); count ( 6 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); value ( perl_version_introduced => [], '5.005' ); value ( perl_version_removed => [], undef ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); value ( perl_version_introduced => [], '5.008003' ); value ( perl_version_removed => [], undef ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); value ( perl_version_introduced => [], '5.008003' ); value ( perl_version_removed => [], undef ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); parse ( "qr ${delim}x$delim" ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); value ( perl_version_introduced => [], '5.008003' ); value ( perl_version_removed => [], undef ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 'qr' ); value ( perl_version_introduced => [], '5.005' ); value ( perl_version_removed => [], undef ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 2, start => [] ); count ( 1 ); choose ( child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( $delim ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); } # The following is not needed at this point, but will be if I add any # tests below here that do not need the somewhat-expensive character # replacement machinery. replace_characters(); done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/t/unit-adhoc.t000444000765000024 527214151156043 15504 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use Test::More 0.88; # Because of done_testing(); use PPI::Document; use PPIx::Regexp; use Scalar::Util qw{ refaddr }; { note 'Test static method PPIx::Regexp->extract_regexps()'; my $doc = PPI::Document->new( 'eg/predump' ); my @re = PPIx::Regexp->extract_regexps( $doc ); cmp_ok scalar @re, '==', 2, 'Found two regexps'; is $re[0]->content(), 'qr{ \\s* , \\s* }smx', q; is $re[1]->content(), 's/ \\\\\\\\ /\\\\/smxg', q; } { note 'Setup for testing statement()'; my $code = 'm/x/;'; my $doc = PPI::Document->new( \$code ); my @stmt = @{ $doc->find( 'PPI::Statement' ) || [] }; cmp_ok scalar @stmt, '==', 1, "'$code' contains exactly 1 statement"; cmp_ok $stmt[0]->content(), 'eq', $code, "That statement is '$code'"; my @re = PPIx::Regexp->extract_regexps( $doc ); cmp_ok scalar @re, '==', 1, "'$code' contains one regexp"; cmp_ok $re[0]->content(), 'eq', 'm/x/', q; my @lit = @{ $re[0]->find( 'PPIx::Regexp::Token::Literal' ) || [] }; cmp_ok scalar @lit, '==', 1, q<'m/x/' contains exactly one literal>; note 'Test statement()'; my $got_stmt = $lit[0]->statement(); ok $got_stmt, 'statement() called on literal returned something'; # The following is what this block is all about. cmp_ok refaddr( $got_stmt ), '==', refaddr( $stmt[0] ), 'statement() called on literal returned original PPI statement'; is scalar PPIx::Regexp->new( 'm/x/' )->statement(), undef, 'statement() returns nothing if regexp did not come from PPI::Document'; } { note 'Normalizing content for ppi()'; use PPIx::Regexp::Tokenizer; my %arg = ( tokenizer => PPIx::Regexp::Tokenizer->new( '' ), ); foreach my $short ( qw{ Code Interpolation } ) { my $class = "PPIx::Regexp::Token::$short"; foreach my $data ( { input => '$foo' }, { input => '$foo[42]' }, { input => '$foo->{bar}' }, { input => '$foo->*@' }, { input => '$foo->*[ 2 .. 4 ]' }, { input => '${foo}', Interpolation => '$foo' }, { input => '${ foo }', Interpolation => '$foo' }, { input => '$${foo}', Interpolation => '$$foo' }, { input => '@${foo}', Interpolation => '@$foo' }, { input => '@{[foo]}' }, { input => '$#foo' }, ) { my $got = $class->__new( $data->{input}, %arg )-> __ppi_normalize_content(); my $want = defined $data->{$short} ? $data->{$short} : defined $data->{want} ? $data->{want} : $data->{input}; is $got, $want, "$short normalizes '$data->{input}' to '$want'"; } } } done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/t/unit.t000444000765000024 21067314151156043 14473 0ustar00tomstaff000000000000package main; use strict; use warnings; use lib qw{ inc }; use PPI::Document; use My::Module::Test; use PPIx::Regexp::Constant qw{ MINIMUM_PERL }; use Scalar::Util qw{ refaddr }; local @PPIx::Regexp::Constant::CARP_NOT = ( @PPIx::Regexp::Constant::CARP_NOT, 'My::Module::Test' ); my $is_ascii = ord( "\t" ) == 9; # per perlebcdic my $have_charnames; BEGIN { eval { require charnames; charnames->import( qw{ :full } ); $have_charnames = charnames->can( 'vianame' ); }; } tokenize( {}, '-notest' ); # We don't know how to tokenize a hash reference. equals ( undef, 'We did not get an object' ); value ( errstr => [], 'HASH not supported' ); parse ( {}, '-notest' ); # If we can't tokenize it, we surely can't parse it. equals ( undef, 'We did not get an object' ); value ( errstr => [], 'HASH not supported' ); parse ( 'fubar' ); # We can't make anything of this. value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); value ( capture_names => [], undef ); value ( max_capture_number => [], undef ); value ( source => [], 'fubar' ); tokenize( '/$x{$y{z}}/' ); count ( 5 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$x{$y{z}}' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); { # The navigation tests get done in their own local scope so that all # the object references we have held go away when we are done. parse ( '/ ( 1 2(?#comment)) /x' ); value ( failures => [], 0 ); value ( errstr => [], undef ); klass ( 'PPIx::Regexp' ); value ( elements => [], 3 ); choose ( first_element => [] ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( last_element => [] ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'x' ); choose ( tokens => [] ); count ( 13 ); navigate( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '2' ); my $lit1 = choose( find_first => 'Token::Literal' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '1' ); true ( significant => [] ); false ( whitespace => [] ); false ( comment => [] ); navigate( next_sibling => [] ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); false ( significant => [] ); true ( whitespace => [] ); false ( comment => [] ); my $lit2 = navigate( next_sibling => [] ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '2' ); navigate( previous_sibling => [] ); navigate( previous_sibling => [] ); equals ( $lit1, 'Two previouses undo two nexts' ); navigate( snext_sibling => [] ); equals ( $lit2, 'A snext gets us the next significant token' ); navigate( sprevious_sibling => [] ); equals ( $lit1, 'An sprevious gets us back' ); navigate( previous_sibling => [] ); equals ( undef, 'Nobody before the first literal' ); navigate( $lit2, next_sibling => [] ); klass ( 'PPIx::Regexp::Token::Comment' ); content ( '(?#comment)' ); false ( significant => [] ); false ( whitespace => [] ); true ( comment => [] ); navigate( next_sibling => [] ); equals ( undef, 'Nobody after second whitespace' ); navigate( $lit2, snext_sibling => [] ); equals ( undef, 'Nobody significant after second literal' ); navigate( $lit1, sprevious_sibling => [] ); equals ( undef, 'Nobody significant before first literal' ); navigate( $lit1, parent => [] ); klass ( 'PPIx::Regexp::Structure::Capture' ); my $top = navigate( top => [] ); klass ( 'PPIx::Regexp' ); true ( ancestor_of => $lit1 ); true ( contains => $lit1 ); false ( ancestor_of => undef ); navigate( $lit1 ); true ( descendant_of => $top ); false ( descendant_of => $lit2 ); false ( ancestor_of => $lit2 ); false ( descendant_of => undef ); choose ( find => 'Token::Literal' ); count ( 2 ); navigate( -1 ); equals ( $lit2, 'The last literal is the second one' ); choose ( find_parents => 'Token::Literal' ); count ( 1 ); my $capt = navigate( 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); value ( elements => [], 7 ); value ( name => [], undef ); navigate( $capt, first_element => [] ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); navigate( $capt, last_element => [] ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); navigate( $capt, schildren => [] ); count ( 2 ); navigate( 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '2' ); choose ( find => sub { ref $_[1] eq 'PPIx::Regexp::Token::Literal' or return 0; $_[1]->content() eq '2' or return 0; return 1; } ); count ( 1 ); navigate( 0 ); equals ( $lit2, 'We found the second literal again' ); navigate( parent => [], schild => 1 ); equals ( $lit2, 'The second significant child is the second literal' ); navigate( parent => [], schild => -2 ); equals ( $lit1, 'The -2nd significant child is the first literal' ); choose ( previous_sibling => [] ); equals ( undef, 'The top-level object has no previous sibling' ); choose ( sprevious_sibling => [] ); equals ( undef, 'The top-level object has no significant previous sib' ); choose ( next_sibling => [] ); equals ( undef, 'The top-level object has no next sibling' ); choose ( snext_sibling => [] ); equals ( undef, 'The top-level object has no significant next sibling' ); choose ( find => [ {} ] ); equals ( undef, 'Can not find a hash reference' ); navigate( $lit2 ); value ( nav => [], [ child => [1], child => [0], child => [2] ] ); } SKIP: { # The cache tests get done in their own scope to ensure the objects # are destroyed. my $num_tests = 8; my $doc = PPI::Document->new( \'m/foo/smx' ) or skip( 'Failed to create PPI::Document', $num_tests ); my $m = $doc->find_first( 'PPI::Token::Regexp::Match' ) or skip( 'Failed to find PPI::Token::Regexp::Match', $num_tests ); my $o1 = PPIx::Regexp->new_from_cache( $m ); my $o2 = PPIx::Regexp->new_from_cache( $m ); equals( $o1, $o2, 'new_from_cache() same object' ); cache_count( 1 ); PPIx::Regexp->flush_cache( 42 ); # Anything not a PPIx::Regexp cache_count( 1 ); # Nothing happens my $o9 = PPIx::Regexp->new_from_cache( '/foo/' ); cache_count( 1 ); # Not cached. $o9->flush_cache(); cache_count( 1 ); # Not flushed, either. PPIx::Regexp->flush_cache(); cache_count(); $o1 = PPIx::Regexp->new_from_cache( $m ); cache_count( 1 ); $o1->flush_cache(); cache_count(); } SKIP: { # More cache tests, in their own scope not only to ensure object # destruction, but so $DISABLE_CACHE can be localized. local $PPIx::Regexp::DISABLE_CACHE = 1; my $num_tests = 2; my $doc = PPI::Document->new( \'m/foo/smx' ) or skip( 'Failed to create PPI::Document', $num_tests ); my $m = $doc->find_first( 'PPI::Token::Regexp::Match' ) or skip( 'Failed to find PPI::Token::Regexp::Match', $num_tests ); my $o1 = PPIx::Regexp->new_from_cache( $m ); my $o2 = PPIx::Regexp->new_from_cache( $m ); different( $o1, $o2, 'new_from_cache() same object, cache disabled' ); cache_count(); # Should still be nothing in cache. } tokenize( '/\\n\\04\\xff\\x{0c}\\N{LATIN SMALL LETTER E}\\N{U+61}/' ); count ( 10 ); choose ( 2 ); value ( ordinal => [], ord "\n" ); choose ( 3 ); value ( ordinal => [], ord "\04" ); choose ( 4 ); value ( ordinal => [], ord "\xff" ); choose ( 5 ); value ( ordinal => [], ord "\x{0c}" ); SKIP: { $have_charnames or skip( "unable to load charnames::vianame", 1 ); choose ( 6 ); value ( ordinal => [], ord 'e' ); } choose ( 7 ); value ( ordinal => [], ord 'a' ); tokenize( 's/\\b/\\b/' ); count ( 7 ); choose ( 4 ); value ( ordinal => [], ord "\b" ); tokenize( '//smx' ); count ( 4 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'smx' ); true ( asserts => 's' ); true ( asserts => 'm' ); true ( asserts => 'x' ); false ( negates => 'i' ); tokenize( '//r' ); count ( 4 ); choose ( 3 ); content ( 'r' ); true ( asserts => 'r' ); value ( match_semantics => [], undef ); value ( perl_version_introduced => [], 5.013002 ); tokenize( '/(?^:foo)/' ); count ( 10 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?^:' ); true ( asserts => 'd' ); false ( asserts => 'l' ); false ( asserts => 'u' ); true ( negates => 'i' ); true ( negates => 's' ); true ( negates => 'm' ); true ( negates => 'x' ); value ( match_semantics => [], 'd' ); value ( perl_version_introduced => [], 5.013006 ); tokenize( '/(?^l:foo)/' ); count ( 10 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?^l:' ); false ( asserts => 'd' ); true ( asserts => 'l' ); false ( asserts => 'u' ); value ( match_semantics => [], 'l' ); value ( perl_version_introduced => [], 5.013006 ); tokenize( 'qr/foo{3}/' ); count ( 10 ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); false ( can_be_quantified => [] ); true ( is_quantifier => [] ); tokenize( 'qr/foo{3,}/' ); count ( 11 ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); false ( can_be_quantified => [] ); true ( is_quantifier => [] ); tokenize( 'qr/foo{3,5}/' ); count ( 12 ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); false ( can_be_quantified => [] ); true ( is_quantifier => [] ); tokenize( 'qr/foo{,3}/' ); count ( 11 ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); false ( can_be_quantified => [] ); true ( is_quantifier => [] ); # As of 5.33.6; previously false tokenize( '/{}/' ); count ( 6 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); tokenize( '/x{}/' ); count ( 7 ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '}' ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); tokenize( '/{2}/' ); count ( 7 ); choose ( 4 ); content ( '}' ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); tokenize( '/\\1?\\g{-1}*\\k{1,3}+/' ); count ( 15 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); value ( perl_version_introduced => [], MINIMUM_PERL ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{-1}' ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); value ( perl_version_introduced => [], '5.009005' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k' ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); value ( perl_version_introduced => [], '5.009005' ); tokenize( '/\\\\d{3,5}+.*?/' ); count ( 15 ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); value ( perl_version_introduced => [], '5.009005' ); choose ( 12 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '?' ); value ( perl_version_introduced => [], MINIMUM_PERL ); tokenize( '/(?bar)/' ); count ( 10 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); value ( name => [], 'foo' ); value ( perl_version_introduced => [], '5.009005' ); tokenize( '/(?\'for\'bar)/' ); count ( 10 ); choose ( 3 ); value ( name => [], 'for' ); value ( perl_version_introduced => [], '5.009005' ); tokenize( '/(?Pbar)/' ); count ( 10 ); choose ( 3 ); value ( name => [], 'fur' ); value ( perl_version_introduced => [], '5.009005' ); tokenize( '/(*PRUNE:foo)x/' ); count ( 6 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Backtrack' ); content ( '(*PRUNE:foo)' ); value ( perl_version_introduced => [], '5.009005' ); tokenize( 's/\\bfoo\\Kbar/baz/' ); count ( 16 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\b' ); value ( perl_version_introduced => [], MINIMUM_PERL ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '\\K' ); value ( perl_version_introduced => [], '5.009005' ); tokenize( '/(*PRUNE:foo)x/' ); count ( 6 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Backtrack' ); content ( '(*PRUNE:foo)' ); value ( perl_version_introduced => [], '5.009005' ); tokenize( '/(?|(foo))/' ); count ( 12 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::BranchReset' ); content ( '?|' ); value ( perl_version_introduced => [], '5.009005' ); parse ( '/[a-z]/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( regular_expression => [] ); true ( interpolates => [] ); choose ( find_first => 'PPIx::Regexp::Structure::CharClass' ); klass ( 'PPIx::Regexp::Structure::CharClass' ); false ( negated => [] ); parse ( 'm\'[^a-z]\'' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( regular_expression => [] ); false ( interpolates => [] ); choose ( find_first => 'PPIx::Regexp::Structure::CharClass' ); klass ( 'PPIx::Regexp::Structure::CharClass' ); true ( negated => [] ); parse ( '/(?|(?foo(wah))|(bar))(hoo)/' ); value ( failures => [], 0 ); value ( max_capture_number => [], 3 ); value ( capture_names => [], [ 'baz' ] ); value ( perl_version_introduced => [], '5.009005' ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::BranchReset' ); count ( 3 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::BranchReset' ); content ( '?|' ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure::NamedCapture' ); count ( 4 ); value ( number => [], 1 ); value ( name => [], 'baz' ); choose ( child => 1, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); content ( '?' ); choose ( child => 1, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0, child => 3 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 3 ); value ( number => [], 2 ); choose ( child => 1, child => 0, child => 0, child => 3, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 0, child => 3, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, child => 0, child => 3, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 3 ); value ( number => [], 1 ); choose ( child => 1, child => 0, child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 2, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 3 ); value ( number => [], 3 ); choose ( child => 1, child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); parse ( 's/(foo)/${1}bar/g' ); klass ( 'PPIx::Regexp' ); value ( failures => [], 0 ); value ( max_capture_number => [], 1 ); value ( capture_names => [], [] ); value ( perl_version_introduced => [], MINIMUM_PERL ); count ( 4 ); choose ( type => 0 ); content ( 's' ); choose ( regular_expression => [] ); content ( '/(foo)/' ); choose ( replacement => [] ); content ( '${1}bar/' ); choose ( modifier => [] ); content ( 'g' ); tokenize( '/((((((((((x))))))))))\\10/' ); count ( 26 ); choose ( 23 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\10' ); parse ( '/((((((((((x))))))))))\\10/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\10' ); tokenize( '/(((((((((x)))))))))\\10/' ); count ( 24 ); choose ( 21 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\10' ); parse ( '/(((((((((x)))))))))\\10/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\10' ); parse ( '/(x)\\1/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\1' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 1 ); parse ( '/(x)\\g1/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g1' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 1 ); parse ( '/(x)\\g-1/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => 0 ); klass ( undef ); content ( undef ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g-1' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], -1 ); parse ( '/(x)\\g{1}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{1}' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 1 ); parse ( '/(x)\\g{-1}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{-1}' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], -1 ); parse ( '/(?\d+)\\g{foo}/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\g{foo}' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'foo' ); value ( number => [], undef ); parse ( '/(?)\\k/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'foo' ); value ( number => [], undef ); parse ( '/(?\d+)\\k\'foo\'/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '\\k\'foo\'' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'foo' ); value ( number => [], undef ); parse ( '/(?\d+)(?P=foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Backreference' ); content ( '(?P=foo)' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'foo' ); value ( number => [], undef ); parse ( '/(?1)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?1)' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 1 ); parse ( '/(x)(?-1)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 2 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?-1)' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], -1 ); parse ( '/(x)(?+1)(y)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?+1)' ); value ( absolute => [], 2 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], '+1' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Structure::Capture' ); parse ( '/(?R)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?R)' ); value ( absolute => [], 0 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 0 ); parse ( '/(?&foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?&foo)' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'foo' ); value ( number => [], undef ); parse ( '/(?P>foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Recursion' ); content ( '(?P>foo)' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'foo' ); value ( number => [], undef ); parse ( '/(?(1)foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); value ( perl_version_introduced => [], '5.005' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(1)' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 1 ); parse ( '/(?(R1)foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); value ( perl_version_introduced => [], '5.009005' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(R1)' ); value ( absolute => [], 1 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 1 ); parse ( '/(?()foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); value ( perl_version_introduced => [], '5.009005' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '()' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'bar' ); value ( number => [], undef ); parse ( '/(?(\'bar\')foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); value ( perl_version_introduced => [], '5.009005' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(\'bar\')' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'bar' ); value ( number => [], undef ); parse ( '/(?(R&bar)foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); value ( perl_version_introduced => [], '5.009005' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(R&bar)' ); value ( absolute => [], undef ); true ( is_named => [] ); value ( name => [], 'bar' ); value ( number => [], undef ); parse ( '/(?(DEFINE)foo)/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Switch' ); count ( 4 ); value ( perl_version_introduced => [], '5.009005' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Condition' ); content ( '(DEFINE)' ); value ( absolute => [], 0 ); false ( is_named => [] ); value ( name => [], undef ); value ( number => [], 0 ); tokenize( '/(?p{ code })/' ); count ( 8 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); value ( perl_version_removed => [], undef ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?p' ); value ( perl_version_removed => [], '5.009005' ); parse ( '/(?p{ code })/' ); value ( failures => [], 0); klass ( 'PPIx::Regexp' ); value ( perl_version_removed => [], '5.009005' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); value ( perl_version_removed => [], undef ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); value ( perl_version_removed => [], '5.009005' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Code' ); count ( 1 ); value ( perl_version_removed => [], '5.009005' ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); value ( perl_version_removed => [], undef ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Code' ); content ( '?p' ); value ( perl_version_removed => [], '5.009005' ); parse ( 'qr{foo}smx' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); choose ( regular_expression => [] ); klass ( 'PPIx::Regexp::Structure::Regexp' ); value ( delimiters => [], '{}' ); choose ( top => [] ); klass ( 'PPIx::Regexp' ); value ( delimiters => [], '{}' ); value ( delimiters => 1, undef ); parse ( 's[bar]smx' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); choose ( regular_expression => [] ); klass ( 'PPIx::Regexp::Structure::Regexp' ); value ( delimiters => [], '<>' ); choose ( top => [], replacement => [] ); klass ( 'PPIx::Regexp::Structure::Replacement' ); value ( delimiters => [], '[]' ); choose ( top => [] ); klass ( 'PPIx::Regexp' ); value ( delimiters => 0, '<>' ); value ( delimiters => 1, '[]' ); parse ( 's/foo/bar/smx' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); value ( delimiters => 0, '//' ); value ( delimiters => 1, '//' ); tokenize( '/foo/', encoding => 'utf8' ); value ( failures => [], 0 ); count ( 7 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); tokenize( 'm/\\N\\n/' ); count ( 6 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\N' ); value ( perl_version_introduced => [], '5.011' ); value ( perl_version_removed => [], undef ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\n' ); value ( perl_version_introduced => [], MINIMUM_PERL ); value ( perl_version_removed => [], undef ); tokenize( '/\\p{ Match = lo-ose }/' ); count ( 5 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\p{ Match = lo-ose }' ); value ( perl_version_introduced => [], '5.006001' ); value ( perl_version_removed => [], undef ); tokenize( '/\\pL/' ); count ( 5 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\pL' ); value ( perl_version_introduced => [], '5.006001' ); value ( perl_version_removed => [], undef ); parse ( 'm{)}smx' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); value ( delimiters => 0, '{}' ); parse ( 's/(\\d+)/roman($1)/ge' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 4 ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( 'roman($1)' ); value ( perl_version_introduced => [], '5.000' ); value ( perl_version_removed => [], undef ); tokenize( '/${foo}bar/' ); count ( 8 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '${foo}' ); ppi ( '$foo' ); { parse ( 's/x/$1/e' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '$1' ); value ( ppi => [], PPI::Document->new( \'$1' ) ); my $doc1 = result(); value ( ppi => [], PPI::Document->new( \'$1' ) ); my $doc2 = result(); cmp_ok( refaddr( $doc1 ), '==', refaddr( $doc2 ), 'Ensure we get back the same object from both calls to ppi()' ); } ##tokenize( '/[[:lower:]]/' ); ##count ( 7 ); ##choose ( 3 ); ##klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); ##content ( '[:lower:]' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##true ( is_case_sensitive => [] ); ## ##parse ( '/[[:lower:]]/' ); ##value ( failures => [], 0 ); ##klass ( 'PPIx::Regexp' ); ##count ( 3 ); ##choose ( child => 1 ); ##klass ( 'PPIx::Regexp::Structure::Regexp' ); ##count ( 1 ); ##choose ( child => 1, child => 0 ); ##klass ( 'PPIx::Regexp::Structure::CharClass' ); ##count ( 1 ); ##choose ( child => 1, child => 0, child => 0 ); ##klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); ##content ( '[:lower:]' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##true ( is_case_sensitive => [] ); ## ##tokenize( '/[[:alpha:]]/' ); ##count ( 7 ); ##choose ( 3 ); ##klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); ##content ( '[:alpha:]' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##false ( is_case_sensitive => [] ); ## ##parse ( '/[[:alpha:]]/' ); ##value ( failures => [], 0 ); ##klass ( 'PPIx::Regexp' ); ##count ( 3 ); ##choose ( child => 1 ); ##klass ( 'PPIx::Regexp::Structure::Regexp' ); ##count ( 1 ); ##choose ( child => 1, child => 0 ); ##klass ( 'PPIx::Regexp::Structure::CharClass' ); ##count ( 1 ); ##choose ( child => 1, child => 0, child => 0 ); ##klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); ##content ( '[:alpha:]' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##false ( is_case_sensitive => [] ); ## ##tokenize( '/\\p{Lower}/' ); ##count ( 5 ); ##choose ( 2 ); ##klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); ##content ( '\\p{Lower}' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##true ( is_case_sensitive => [] ); ## ##parse ( '/\\p{Lower}/' ); ##value ( failures => [], 0 ); ##klass ( 'PPIx::Regexp' ); ##count ( 3 ); ##choose ( child => 1 ); ##klass ( 'PPIx::Regexp::Structure::Regexp' ); ##count ( 1 ); ##choose ( child => 1, child => 0 ); ##klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); ##content ( '\\p{Lower}' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##true ( is_case_sensitive => [] ); ## ##tokenize( '/\\p{Alpha}/' ); ##count ( 5 ); ##choose ( 2 ); ##klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); ##content ( '\\p{Alpha}' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##false ( is_case_sensitive => [] ); ## ##parse ( '/\\p{Alpha}/' ); ##value ( failures => [], 0 ); ##klass ( 'PPIx::Regexp' ); ##count ( 3 ); ##choose ( child => 1 ); ##klass ( 'PPIx::Regexp::Structure::Regexp' ); ##count ( 1 ); ##choose ( child => 1, start => [] ); ##count ( 1 ); ##choose ( child => 1, child => 0 ); ##klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); ##content ( '\\p{Alpha}' ); ##true ( significant => [] ); ##true ( can_be_quantified => [] ); ##false ( is_quantifier => [] ); ##false ( is_case_sensitive => [] ); parse ( '/ . /' ); false ( modifier_asserted => 'u' ); false ( modifier_asserted => 'x' ); parse ( '/ . /', default_modifiers => [ 'smxu' ] ); true ( modifier_asserted => 'u' ); false ( modifier_asserted => 'l' ); true ( modifier_asserted => 'x' ); parse ( '/ . /', default_modifiers => [ 'smxu', '-u' ] ); false ( modifier_asserted => 'u' ); false ( modifier_asserted => 'l' ); true ( modifier_asserted => 'x' ); # This to be sure we recognize 'aa' when consecutive. parse ( '/ . /aasmx' ); value ( failures => [], 0 ); true ( modifier_asserted => 'aa' ); false ( modifier_asserted => 'a' ); # Bug reported by Anonymous Monk. /aia is equivalent to /aai parse ( '/ . /asmxa' ); value ( failures => [], 0 ); true ( modifier_asserted => 'aa' ); false ( modifier_asserted => 'a' ); # Wishlist by Anonymous Monk to know what modifiers were asserted where. parse ( '/foo/i' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); true ( modifier_asserted => 'i' ); parse ( '/(?i)foo/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 4 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); true ( modifier_asserted => 'i' ); parse ( '/(?i:foo)/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 3 ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); true ( modifier_asserted => 'i' ); parse ( '/foo/', default_modifiers => [ 'i' ] ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); true ( modifier_asserted => 'i' ); parse ( '/(?-i:foo)/i' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Modifier' ); count ( 3 ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); false ( modifier_asserted => 'i' ); # End of wishlist by Anonymous Monk to know what modifiers were asserted where. # Handle leading and trailing white space parse ( ' /foo/ ' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 5 ); value ( delimiters => [], '//' ); # The purpose of these choose ( modifier => [] ); # tests is to ensure klass ( 'PPIx::Regexp::Token::Modifier' ); # that the significant value ( content => [], '' ); # parts of the regexp choose ( regular_expression => [] ); # can still be found if klass ( 'PPIx::Regexp::Structure::Regexp' ); # we introduce leading choose ( type => 0 ); # and trailing white klass ( 'PPIx::Regexp::Token::Structure' ); # space value ( content => [], '' ); # ... choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 2, start => [] ); count ( 1 ); choose ( child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, type => [] ); count ( 0 ); choose ( child => 2, finish => [] ); count ( 1 ); choose ( child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 2, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); choose ( child => 4 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); # RT #82140: incorrect parsing of (\?|...) - Alexandr Ciornii tokenize( '/(\\?|I)/' ); value ( failures => [], 0 ); count ( 9 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\?' ); tokenize( '?(\\?|I)?' ); value ( failures => [], 0 ); count ( 8 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::BranchReset' ); content ( '\\?|' ); # RT #82140: incorrect parsing of (\?>...) - Alexandr Ciornii tokenize( '/(\\?>I)/' ); value ( failures => [], 0 ); count ( 9 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\?' ); tokenize( '?(\\?>I)?' ); value ( failures => [], 0 ); count ( 8 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Subexpression' ); content ( '\\?>' ); # RT #82140: incorrect parsing of (\?:...) - Alexandr Ciornii tokenize( '/(\\?:I)/' ); value ( failures => [], 0 ); count ( 9 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\?' ); tokenize( '?(\\?:I)?' ); value ( failures => [], 0 ); count ( 8 ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '\\?:' ); # RT 91798: non-breaking space should not be whitespace - Nobuo Kumagai tokenize( "/\240/x" ); value ( failures => [], 0 ); count ( 5 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( "\240" ); note '/ee should parse like /e'; tokenize( 's/foo/bar(42)/ee' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( 's' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( 'bar(42)' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( 'ee' ); # RT 107331 - Bogus trailing characters should cause error - Klaus Rindfrey parse ( '/foo/|' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 4 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); choose ( child => 3 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '|' ); error ( 'Trailing characters after expression' ); choose ( 'modifier' => [] ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # As of Perl 5.23.4, only space and horizontal tab can be parsed as # whitespace inside a bracketed character klass inside an extended # bracketed character klass. tokenize( "/(?[ [\f] ])/" ); count ( 11 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(?[' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( "\f" ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Whitespace' ); content ( ' ' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '])' ); choose ( 9 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 10 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # Ensure that the error gets cleared when a PPIx::Regexp::Token::Unknown # gets reblessed into something useful. parse ( '/{?+}/' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Quantifier' ); content ( '?' ); error ( undef ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Token::Greediness' ); content ( '+' ); error ( undef ); # \U and friends are still metacharacters inside \Q tokenize( '/\\Q\\Ux\\Ey/' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\Q' ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\U' ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Control' ); content ( '\\E' ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'y' ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # Need to report an error if the switch condition can not be deciphered. parse ( '/(?([w]))/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::Unknown' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, type => [] ); count ( 1 ); choose ( child => 1, child => 0, type => 0 ); klass ( 'PPIx::Regexp::Token::GroupType::Switch' ); content ( '?' ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure::Capture' ); count ( 1 ); choose ( child => 1, child => 0, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, child => 0, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, child => 0, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 0, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'w' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # Make sure we do not prematurely end an extended bracketed character # class if we encounter a bracketed character class followed immediately # by the end of a parenthesized group (e.g. in '(?[([x])])' the extended # class should end at the end of the string). parse ( '/(?[(\\w-[[:lower:]])|\\p{Greek}])|[^a-z]/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 3 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::RegexSet' ); count ( 3 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(?[' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '])' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Structure' ); count ( 3 ); choose ( child => 1, child => 0, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); choose ( child => 1, child => 0, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\w' ); choose ( child => 1, child => 0, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 0, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, child => 0, child => 2, start => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, child => 0, child => 2, type => [] ); count ( 0 ); choose ( child => 1, child => 0, child => 0, child => 2, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, child => 0, child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0, child => 2, child => 0 ); klass ( 'PPIx::Regexp::Token::CharClass::POSIX' ); content ( '[:lower:]' ); choose ( child => 1, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::CharClass::Simple' ); content ( '\\p{Greek}' ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '|' ); choose ( child => 1, child => 2 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 2, start => [] ); count ( 1 ); choose ( child => 1, child => 2, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 2, type => [] ); count ( 1 ); choose ( child => 1, child => 2, type => 0 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '^' ); choose ( child => 1, child => 2, finish => [] ); count ( 1 ); choose ( child => 1, child => 2, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 2, child => 0 ); klass ( 'PPIx::Regexp::Node::Range' ); count ( 3 ); choose ( child => 1, child => 2, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); choose ( child => 1, child => 2, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); choose ( child => 1, child => 2, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); # Make sure we record the correct number of captures in the presence of # the /n qualifier. note 'Correct number of captures in presence of /n qualifier'; parse ( '/(foo)/n' ); value ( max_capture_number => [], 0 ); parse ( '/(?foo)/n' ); value ( max_capture_number => [], 1 ); # ?foo? without a specific type has been removed as of 5.21.1. These # would be in t/version.t except that the limit is not on a single # token but on the combination of empty type and question mark # delimiters. note '?foo? without explicit type is removed in 5.21.1'; parse ( '?foo?' ); value ( perl_version_removed => [], 5.021001 ); parse ( 'm?foo?' ); value ( perl_version_removed => [], undef ); # postderef note 'postderef was added experimentally in 5.19.5'; tokenize( '/$x->$*foo/' ); count ( 8 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$x->$*' ); tokenize( '/$x->$#*foo/' ); count ( 8 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$x->$#*' ); tokenize( '/$x->@*foo/' ); count ( 8 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$x->@*' ); tokenize( '/$x->@[1,2]/' ); count ( 5 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Interpolation' ); content ( '$x->@[1,2]' ); tokenize( 's/x/$x->%{foo,bar}/e' ); count ( 7 ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Code' ); content ( '$x->%{foo,bar}' ); # \x{} note '/\\x{}/ generates a NUL by default'; parse ( '/\\x{}/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\x{}' ); value ( ordinal => [], 0 ); note q; parse ( '/\\x{}/', strict => 1 ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\x{}' ); error ( 'Empty \\x{} is an error under "use re \'strict\'"' ); # \o{} note '/\\o{}/ is an error'; parse ( '/\\o{}/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\o{}' ); error ( 'Empty \\o{} is an error' ); note '/\\o{ }/ is not normally an error'; parse ( '/\\o{ }/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\o{ }' ); value ( ordinal => [], 0 ); note q; parse ( '/\\o{ }/', strict => 1 ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\o{ }' ); error ( 'Non-octal character in \\o{...}' ); # \p{} note q; parse ( '/\\p{ }/' ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\p{ }' ); error ( 'Empty \\p{} is an error' ); note 'Make sure \Q stacks with \U, \L and \F'; tokenize( '/\\Qx\\Uy\\E\\w\\E/' ); count ( 11 ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '\\w' ); note 'use re qw{ strict }'; tokenize( '/\\N{}/', strict => 1 ); count ( 5 ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Unknown' ); content ( '\\N{}' ); error ( 'Empty Unicode character name' ); value ( perl_version_introduced => [], '5.023008' ); value ( perl_version_removed => [], '5.027001' ); parse ( '/[A-z]/', strict => 1 ); value ( failures => [], 1 ); klass ( 'PPIx::Regexp' ); count ( 3 ); value ( perl_version_introduced => [], '5.023008' ); value ( perl_version_removed => [], undef ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Node::Unknown' ); count ( 3 ); error ( 'Non-portable range ends prohibited by "use re \'strict\'"' ); value ( perl_version_introduced => [], '5.023008' ); value ( perl_version_removed => [], undef ); note 'next_element(), previous_element()'; parse ( '/(x)/' ); choose ( child => 1, child => 0, child => 0 ); # 'x' navigate( previous_element => [] ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); navigate( next_element => [] ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); note 'snext_element(), sprevious_element()'; parse ( '/ ( x ) /x' ); choose ( child => 1, child => 0, child => 0 ); # 'x' navigate( sprevious_element => [] ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); navigate( snext_element => [] ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'x' ); note 'accepts_perl(), requirements_for_perl()'; parse ( '/x/' ); value ( accepts_perl => [ '5.000' ], 1 ); value ( accepts_perl => [ '5.010001' ], 1 ); value ( requirements_for_perl => [], '5.000 <= $]' ); parse ( '/x/a' ); value ( accepts_perl => [ '5.000' ], 0 ); value ( accepts_perl => [ '5.010001' ], 0 ); value ( accepts_perl => [ '5.013010' ], 1 ); value ( accepts_perl => [ '5.014' ], 1 ); value ( requirements_for_perl => [], '5.013010 <= $]' ); parse ( '/x{/' ); value ( accepts_perl => [ '5.000' ], 1 ); value ( accepts_perl => [ '5.010001' ], 1 ); value ( accepts_perl => [ '5.025000' ], 1 ); value ( accepts_perl => [ '5.025001' ], 0 ); value ( accepts_perl => [ '5.027000' ], 0 ); value ( accepts_perl => [ '5.027001' ], 1 ); value ( requirements_for_perl => [], '5.000 <= $] < 5.025001 || 5.027001 <= $]' ); { my $re = '/ [f g] o o b a (?#comment) r /'; parse ( $re ); value ( scontent => [], '/ [f g] o o b a r /' ); parse ( $re, default_modifiers => [ qw{ x } ] ); value ( scontent => [], '/[f g]oobar/' ); parse ( $re, default_modifiers => [ qw{ xx } ] ); value ( scontent => [], '/[fg]oobar/' ); } { parse ( '/^(?i:foo)$/' ); navigate ( 'first_token' ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '^' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?i:' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); navigate ( 'next_token' ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); ok ! navigate( 'next_token' ), 'There is no next token'; } { parse ( '/^(?i:foo)$/' ); navigate ( 'last_token' ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '$' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ')' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'o' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'f' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::GroupType::Modifier' ); content ( '?i:' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '(' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Assertion' ); content ( '^' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); navigate ( 'previous_token' ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); ok ! navigate( 'previous_token' ), 'There is no next token'; } SKIP: { $is_ascii or skip( 'Non-ASCII machines will have different ordinal values', 10, ); note 'Ordinals'; tokenize( '/foo/', '--notokens' ); dump_result( ordinal => 1, tokens => 1, <<'EOD', q ); PPIx::Regexp::Token::Structure '' PPIx::Regexp::Token::Delimiter '/' PPIx::Regexp::Token::Literal 'f' 0x66 PPIx::Regexp::Token::Literal 'o' 0x6f PPIx::Regexp::Token::Literal 'o' 0x6f PPIx::Regexp::Token::Delimiter '/' PPIx::Regexp::Token::Modifier '' EOD parse ( '/(foo[a-z\\d])/x' ); dump_result( verbose => 1, <<'EOD', q ); PPIx::Regexp failures=0 max_capture_number=1 PPIx::Regexp::Token::Structure '' significant is_matcher=false PPIx::Regexp::Structure::Regexp / ... / max_capture_number=1 is_matcher=true PPIx::Regexp::Structure::Capture ( ... ) number=1 name=undef can_be_quantified is_matcher=true PPIx::Regexp::Token::Literal 'f' 0x66 significant can_be_quantified is_matcher=true PPIx::Regexp::Token::Literal 'o' 0x6f significant can_be_quantified is_matcher=true PPIx::Regexp::Token::Literal 'o' 0x6f significant can_be_quantified is_matcher=true PPIx::Regexp::Structure::CharClass [ ... ] can_be_quantified is_matcher=true PPIx::Regexp::Node::Range PPIx::Regexp::Token::Literal 'a' 0x61 significant can_be_quantified is_matcher=true PPIx::Regexp::Token::Operator '-' significant can_be_quantified is_matcher=false PPIx::Regexp::Token::Literal 'z' 0x7a significant can_be_quantified is_matcher=true PPIx::Regexp::Token::CharClass::Simple '\\d' significant can_be_quantified is_matcher=true PPIx::Regexp::Token::Modifier 'x' significant x is_matcher=false EOD parse ( '/(?\\d+)/' ); dump_result( perl_version => 1, <<'EOD', q\\d+)/'> ); PPIx::Regexp failures=0 5.009005 <= $] PPIx::Regexp::Token::Structure '' 5.000 <= $] PPIx::Regexp::Structure::Regexp / ... / 5.009005 <= $] PPIx::Regexp::Structure::NamedCapture (? ... ) 5.009005 <= $] PPIx::Regexp::Token::CharClass::Simple '\\d' 5.000 <= $] PPIx::Regexp::Token::Quantifier '+' 5.000 <= $] PPIx::Regexp::Token::Modifier '' 5.000 <= $] EOD tokenize( '/[a-z]/', '--notokens' ); dump_result( test => 1, verbose => 1, tokens => 1, <<'EOD', q ); tokenize( '/[a-z]/' ); count ( 9 ); choose ( 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 1 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 2 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 3 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 4 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 5 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 6 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 7 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( 8 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); EOD parse ( '/[a-z]/' ); dump_result( test => 1, verbose => 1, <<'EOD', q ); parse ( '/[a-z]/' ); value ( failures => [], 0 ); klass ( 'PPIx::Regexp' ); count ( 3 ); choose ( child => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( child => 1 ); klass ( 'PPIx::Regexp::Structure::Regexp' ); count ( 1 ); choose ( child => 1, start => [] ); count ( 1 ); choose ( child => 1, start => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, type => [] ); count ( 0 ); choose ( child => 1, finish => [] ); count ( 1 ); choose ( child => 1, finish => 0 ); klass ( 'PPIx::Regexp::Token::Delimiter' ); content ( '/' ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Structure::CharClass' ); count ( 1 ); choose ( child => 1, child => 0, start => [] ); count ( 1 ); choose ( child => 1, child => 0, start => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( '[' ); choose ( child => 1, child => 0, type => [] ); count ( 0 ); choose ( child => 1, child => 0, finish => [] ); count ( 1 ); choose ( child => 1, child => 0, finish => 0 ); klass ( 'PPIx::Regexp::Token::Structure' ); content ( ']' ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Node::Range' ); count ( 3 ); choose ( child => 1, child => 0, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'a' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( child => 1, child => 0, child => 0, child => 1 ); klass ( 'PPIx::Regexp::Token::Operator' ); content ( '-' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( child => 1, child => 0, child => 0, child => 2 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( 'z' ); true ( significant => [] ); true ( can_be_quantified => [] ); false ( is_quantifier => [] ); choose ( child => 2 ); klass ( 'PPIx::Regexp::Token::Modifier' ); content ( '' ); true ( significant => [] ); false ( can_be_quantified => [] ); false ( is_quantifier => [] ); EOD } finis (); done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/t/version.t000444000765000024 13363614151156043 15204 0ustar00tomstaff000000000000# This test instantiates tokens directly rather than through the # tokenizer. It is up to the author of the test to be sure that the # the contents of each token are consistent with its class. There is # also the risk that tokens instantiated directly may be set up # differently than theoretically-equivalent tokens generated by the # tokenizer, in cases where __PPIX_TOKEN__recognize() is used. # # Caveat Auctor. package main; use 5.006; use strict; use warnings; use PPIx::Regexp; use PPIx::Regexp::Constant qw{ HASH_REF SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS }; use Scalar::Util qw{ blessed }; use Test::More 0.88; # NOTE we use this circumlocution to hide the :encoding() from # xt/author/minimum_perl.t and Perl::MinimumVersion. The two-argument # binmode itself is OK under Perl 5.6 but the :encoding() is not. But if # we're 5.6 then SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS is false, # so the binmode() never gets executed. use constant OUTPUT_ENCODING => ':encoding(utf-8)'; if ( SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS ) { my $builder = Test::More->builder(); foreach my $method ( qw{ output failure_output todo_output } ) { my $handle = $builder->$method(); binmode $handle, OUTPUT_ENCODING; } } use charnames qw{ :full }; our $REPORT; # True to report rather than test. # If $REPORT is true, you get instead of a test a CSV-formatted report # of the syntax elements and the versions they were introduced and # retracted. Named arguments to the routines have been added to support # the report -- perhaps too many of them. All are optional. They are: # note => Text describing the thing being reported on. This is # described further in the details of the report. # text => The text of the thing reported on. If applied to the class # it is a sprintf template. The default is the content of the # token. # report => false to suppress the report. If applied to the class it # is a default for all tokens in that class. # The columns of the report are: # Kind => The kind of syntax element. This is the 'note' argument to # the class() subroutine, and defaults to the class name. # Token => The syntax element itself. This is the 'text' argument of # the token() or class() subroutines (the latter suitably # processed), defaulting to the actual content of the syntax # element. # Descr => The description of the syntax element. This is the 'note' # argument of the token() subroutine, defaulting to the empty # string. # Introduced => The version of Perl in which the element was # introduced, from the perl_version_introduced() method of the # token. Since I have not researched Perl 4, and I have no # access to Perls earlier than 5.3, anything in 5.3 is assumed # willy-nilly to be in Perl 5.0. # Ref => The source of the version introduced, if known. Typically a # Perl documentation reference. If a reference other than # perl?*delta, the Perl version of the documentation is # prefaced, and the version is inferred from the fact that the # previous version of the document did not refer to the feature. # The default is the empty string. # Removed => The version of Perl in which the element was removed. # The default is '', indicating that the element is still in # Perl. # Ref => The source of the version removed, if known, and if the # element has in fact been removed. # # One way to use this is # $ perl -e '$REPORT = 1; do "t/version.t";' # Trailing empty fields are removed. use PPIx::Regexp::Constant qw{ COOKIE_CLASS COOKIE_REGEX_SET MINIMUM_PERL }; use PPIx::Regexp::Tokenizer; klass( 'PPIx::Regexp::Token::Assertion', note => 'Assertion' ); token( '^', note => 'Matches beginning of string or after newline' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '$', note => 'Matches end of string or newline' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\b', note => 'Matches word/nonword boundary' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\B', note => 'Opposite of \b' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\A', note => 'Matches beginning of string' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\Z', note => 'Matches end of string, or newline before end' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\G', note => 'Matches at pos()' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\z', note => 'Matches end of string' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '\K', note => 'In s///, keep everything before the \K' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\b{gcb}', note => 'Assert grapheme cluster boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\b{g}', note => 'Assert grapheme cluster boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\b{lb}', note => 'Assert line boundary' ); m_call( perl_version_introduced => '5.023007', note => 'perl5237delta' ); m_call( perl_version_removed => undef ); token( '\b{wb}', note => 'Assert word boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\b{sb}', note => 'Assert sentence boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\B{gcb}', note => 'Assert no grapheme cluster boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\B{g}', note => 'Assert no grapheme cluster boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\B{lb}', note => 'Assert no line boundary' ); m_call( perl_version_introduced => '5.023007', note => 'perl5237delta' ); m_call( perl_version_removed => undef ); token( '\B{wb}', note => 'Assert no word boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); token( '\B{sb}', note => 'Assert no sentence boundary' ); m_call( perl_version_introduced => '5.021009', note => 'perl5219delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Backreference', note => 'Back reference' ); token( '\1', note => 'Back reference to first capture group' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\g1', note => 'Back reference to first capture group' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\g{1}', note => 'Back reference to first capture group' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\g-1', note => 'Back reference to previous capture group' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '\g{-1}', note => 'Back reference to previous capture group' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\k', note => 'Back reference to named capture group' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( q{\k'foo'}, note => 'Back reference to named capture group' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(?P=foo)', note => 'Back reference to named capture group' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Backtrack', note => 'Back tracking control' ); token( '(*THEN)', note => 'Forces next alternation on failure' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '(*PRUNE)', note => 'Prevent backtracking past here on failure' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '(*MARK)', note => 'Name branches of alternation, target for (*SKIP)' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '(*SKIP)', note => 'Like (*PRUNE) but also discards match to this point' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '(*COMMIT)', note => 'Causes match failure when backtracked into on failure' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '(*FAIL)', note => 'Always fails, forcing backtrack' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '(*ACCEPT)', note => 'Causes match to succeed at the point of the (*ACCEPT)' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::CharClass::POSIX', note => 'POSIX character class' ); token( '[:alpha:]', note => 'Match alphabetic' ); m_call( perl_version_introduced => '5.006', note => 'perl56delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::CharClass::Simple', note => 'Character class' ); token( '.', note => 'Match any character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\w', note => 'Match word character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\W', note => 'Match non-word character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\s', note => 'Match white-space character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\S', note => 'Match non-white-space character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\d', note => 'Match decimal digit' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\D', note => 'Match any character but a decimal digit' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\C', note => 'Match a single octet (removed in 5.23.0)' ); m_call( perl_version_introduced => '5.006' ); m_call( perl_version_removed => '5.023' ); token( '\X', note => 'Match a Unicode extended grapheme cluster' ); m_call( perl_version_introduced => '5.006', note => '5.6.0 perlre' ); m_call( perl_version_removed => undef ); token( '\p{Latin}', note => 'Match a character with the given Unicode property' ); m_call( perl_version_introduced => '5.006001', note => 'perl561delta' ); m_call( perl_version_removed => undef ); token( '\h', note => 'Match a horizontal-white-space character' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\H', note => 'Match a non-horizontal-white-space character' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\v', note => 'Match a vertical-white-space character' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\V', note => 'Match a non-vertical-white-space character' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\R', note => 'Match a generic new-line character' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( '\N', note => 'Match any character but a new-line character' ); m_call( perl_version_introduced => '5.011', note => 'perl5110delta' ); m_call( perl_version_removed => undef ); token( '\\p{Script=/Latin|Greek/}', note => 'Match a character with the given wildcard property value' ); m_call( perl_version_introduced => '5.029009', note => 'perl5299delta' ); m_call( perl_version_removed => undef ); token( '\\p{name=/(SMILING|GRINNING) FACE/}' ); klass( 'PPIx::Regexp::Token::Code', note => 'Code', report => 0 ); token( '{foo}', note => 'Code' ); m_call( perl_version_introduced => '5.005' ); # see ::GroupType::Code m_call( perl_version_removed => undef ); token( '$x->&*', note => 'Code with postderef' ); m_call( perl_version_introduced => '5.019005', note => 'perl5195delta' ); m_call( perl_version_removed => undef ); token( '$x->%{foo,bar}', note => 'Code with postderef slice' ); m_call( perl_version_introduced => '5.019005', note => 'perl5195delta' ); m_call( perl_version_removed => undef ); # The( interesting version functionality is on # PPIx::Regexp::Token::GroupType::Code. klass( 'PPIx::Regexp::Token::Comment', note => 'Comment' ); token( '(?#foo)', note => 'Embedded comment' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '# foo', note => 'Extended comment, with /x' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Condition', note => 'Condition' ); token( '(1)', note => 'True if the first capture group matched' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '(R1)', note => 'True if recursing directly inside first capture group' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(R)', note => 'True if recursing' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '()', note => 'True if capture group matched' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( q{('foo')}, note => 'True if capture group matched' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(R&foo)', note => 'True if recursing directly inside capture group ' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(DEFINE)', note => 'Define a group to be recursed into' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Control', note => 'Interpolation control' ); token( '\l', note => 'Lowercase next character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\u', note => 'Uppercase next character' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\L', note => 'Lowercase until \E' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\U', note => 'Uppercase until \E' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\F', note => 'Fold case until \E' ); m_call( perl_version_introduced => '5.015008' ); m_call( perl_version_removed => undef ); token( '\E', note => 'End of interpolation control' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\Q', note => 'Quote interpolated metacharacters until \E' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Delimiter', note => 'Delimiter', report => 0 ); token( '/' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); SKIP: { SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS or skip 'Weird delimiters test requires Perl 5.8.3 or above', 3; token( "\N{COMBINING CIRCUMFLEX ACCENT}" ); m_call( perl_version_introduced => '5.008003', note => 'experimentation' ); m_call( perl_version_removed => '5.029' ); } klass( 'PPIx::Regexp::Token::Greediness', note => 'Greediness' ); token( '?', note => 'Match shortest string first' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '+', note => 'Match longest string and give nothing back' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); # PPIx::Regexp::Token::GroupType: see( the individual subclasses, below. klass( 'PPIx::Regexp::Token::GroupType::Assertion', note => 'Assertion', text => '(%sregexp)' ); token( '?=', note => 'Positive lookahead' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '*pla:', note => 'Positive lookahead' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '*positive_lookahead:', note => 'Positive lookahead' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '?!', note => 'Negative lookahead' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '*nla:', note => 'Negative lookahead' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '*negative_lookahead:', note => 'Negative lookahead' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '?<=', note => 'Positive lookbehind' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '*plb:', note => 'Positive lookbehind' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '*positive_lookbehind:', note => 'Positive lookbehind' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '? 'Negative lookbehind' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '*nlb:', note => 'Negative lookbehind' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); token( '*negative_lookbehind:', note => 'Negative lookbehind' ); m_call( perl_version_introduced => '5.027009', note => '5.27.9 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::BranchReset', note => 'Branch reset' ); token( '?|', note => 'Re-use capture group numbers in branches of alternation', text => '(?|regexp|regexp...)' ); m_call( perl_version_introduced => '5.009005', note => '5.9.5 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Code', note => 'Code', text => '(%s{code})' ); token( '?p', note => 'Function unknown' ); m_call( perl_version_introduced => '5.005', note => 'Undocumented that I can find' ); m_call( perl_version_removed => '5.009005', note => 'perl595delta' ); token( '?', note => 'Evaluate code. Always matches.' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '??', note => 'Evaluate code, use as regexp at this point' ); m_call( perl_version_introduced => '5.006', note => 'perl56delta (not in 5.5.4 perlre)' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Modifier', note => 'Clustering', text => '(%sregexp)' ); token( '?:', note => 'Basic clustering' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '?i:', note => 'Cluster with case-independance' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '?i-x:', note => 'Cluster with case-independance but no extended syntax' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Modifier', note => 'Modifiers', text => '(%s)', report => 0 ); token( '?^i', note => 'Reassert defaults, case-independance' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '?d', note => 'Compile without locale or unicode_strings' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '?l', note => 'Compile with locale' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '?u', note => 'Compile with unicode_strings' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::NamedCapture', note => 'Named capture', text => '(%sregexp)' ); token( '?', note => 'Basic named capture' ); m_call( perl_version_introduced => '5.009005', note => 'perl595delta' ); m_call( perl_version_removed => undef ); token( q{?'foo'}, note => 'Named capture, quoted syntax' ); m_call( perl_version_introduced => '5.009005', note => '5.9.5 perlre' ); m_call( perl_version_removed => undef ); token( '?P', note => 'Named capture, PCRE/Python syntax' ); m_call( perl_version_introduced => '5.009005', note => '5.9.5 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Subexpression', note => 'Subexpression', text => '(%sregexp)' ); token( '?>', note => 'Match subexpression without backtracking' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Switch', note => 'Switch', report => 0 ); # See PPIx::Regexp::Token::Condition token( '?' ); m_call( perl_version_introduced => '5.005' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Interpolation', note => 'Interpolation' ); token( '$foo', note => 'Interpolate the contents of $foo' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '$foo', cookie => COOKIE_REGEX_SET, note => 'Interpolation in regex set' ); m_call( perl_version_introduced => '5.017009', note => 'perl5179delta' ); m_call( perl_version_removed => undef ); token( '$x->@*', note => 'Postfix deref' ); m_call( perl_version_introduced => '5.019005', note => 'perl5195delta' ); m_call( perl_version_removed => undef ); token( '$x->@[1,2]', note => 'Postfix deref slice' ); m_call( perl_version_introduced => '5.019005', note => 'perl5195delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Literal', note => 'Literal' ); token( 'a', note => q{Letter 'a'} ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); token( '\b', note => 'Back space, in character class only' ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); token( '\t', note => 'Horizontal tab' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\n', note => 'New line' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\r', note => 'Return' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\a', note => 'Alarm (bell)' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\e', note => 'Escape' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\033', note => 'Octal 33 = escape, classic' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\o{61}', note => q{Octal 61 = '1', new style} ); m_call( perl_version_introduced => '5.013003', note => 'perl5133delta' ); m_call( perl_version_removed => undef ); token( '\x1B', note => 'Hex 1b = escape, classic' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\x{1b}', note => 'Hex 1b = escape, new style' ); m_call( perl_version_introduced => '5.006', note => '5.6.0 perlre (not in perldelta)' ); m_call( perl_version_removed => undef ); token( '\c[', note => 'Control-[ = escape' ); # ] m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '\N{LATIN SMALL LETTER P}', note => q{Letter 'p', by name} ); m_call( perl_version_introduced => '5.006001', note => 'perl561delta' ); m_call( perl_version_removed => undef ); token( '\N{U+32}', note => q{Digit '2', by Unicode code point} ); m_call( perl_version_introduced => '5.008', note => '5.8.0 charnames' ); m_call( perl_version_removed => undef ); token( '{', note => 'Initial unescaped literal left curly' ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); token( '{', note => 'Unescaped literal left curly', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); # m_call( perl_version_removed => '5.025001' ); note( '/x{/ removed in 5.025001, re-added in 5.027001' ); m_call( perl_version_removed => undef ); token( '\\N{LATIN CAPITAL LETTER A}', main => q<'> ); m_call( perl_version_introduced => '5.029010', note => '5.29.10 perldelta' ); m_call( perl_version_removed => undef ); token( '\s', class => 'PPIx::Regexp::Token::CharClass::Simple' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => '5.025001' ); token( '.', class => 'PPIx::Regexp::Token::CharClass::Simple' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); # Not removed yet token( '\p{Latin}', class => 'PPIx::Regexp::Token::CharClass::Simple' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); # Not removed yet token( '\P{Latin}', class => 'PPIx::Regexp::Token::CharClass::Simple' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); # Not removed yet token( '^', class => 'PPIx::Regexp::Token::Assertion' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); require PPIx::Regexp::Structure::Assertion; token( undef, class => 'PPIx::Regexp::Structure::Assertion' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); require PPIx::Regexp::Structure::CharClass; token( undef, class => 'PPIx::Regexp::Structure::CharClass' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); # Not removed yet require PPIx::Regexp::Structure::RegexSet; token( undef, class => 'PPIx::Regexp::Structure::RegexSet' ); token( '{', previous => 1 ); m_call( perl_version_introduced => MINIMUM_PERL ); m_call( perl_version_removed => undef ); # Not removed yet klass( 'PPIx::Regexp::Token::Modifier', note => 'Operator modifiers', text => '/%s' ); token( 'i', note => 'Case independent' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 's', note => 'Single-line' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'm', note => 'Multiple lines' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'x', note => 'Extended syntax' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'xx', note => 'Extended syntax eveb inside bracketed classes' ); m_call( perl_version_introduced => '5.025009', note => '5.25.9 perldelta' ); m_call( perl_version_removed => undef ); token( 'g', note => 'Global matching' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'o', note => 'Compile once' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'e', note => 'Replacement is expression' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'ee', note => 'Replacement is eval-ed expression' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'c', note => 'Do not reset pos() on failure (with /g)' ); m_call( perl_version_introduced => '5.004', note => '5.4.5 perlop' ); m_call( perl_version_removed => undef ); token( 'p', note => 'Populate ${^PREMATCH}, ${^MATCH}, and ${^POSTMATCH}' ); m_call( perl_version_introduced => '5.009005', note => '5.9.5 perlop' ); m_call( perl_version_removed => undef ); token( 'r', note => 'Return modified string from s///, leaving original unmodified' ); m_call( perl_version_introduced => '5.013002', note => 'perl5132delta' ); m_call( perl_version_removed => undef ); token( 'pi', report => 0 ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( 'pir', report => 0 ); m_call( perl_version_introduced => '5.013002' ); m_call( perl_version_removed => undef ); token( 'a', note => 'Match like /u, but restrict non-Unicode classes to ASCII' ); m_call( perl_version_introduced => '5.013010', note => 'perl51310delta' ); m_call( perl_version_removed => undef ); token( 'aa', note => 'Match like /a, and do not match ASCII and non-ASCII literals' ); m_call( perl_version_introduced => '5.013010', note => 'perl51310delta' ); m_call( perl_version_removed => undef ); token( 'd', note => 'Match using default (pre-5.13.10) semantics' ); m_call( perl_version_introduced => '5.013010', note => 'perl51310delta' ); m_call( perl_version_removed => undef ); token( 'l', note => 'Match using current locale semantics' ); m_call( perl_version_introduced => '5.013010', note => 'perl51310delta' ); m_call( perl_version_removed => undef ); token( 'u', note => 'Match using Unicode semantics' ); m_call( perl_version_introduced => '5.013010', note => 'perl51310delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Modifier', note => 'Embedded modifiers' ); token( '(?i)', note => 'Basic modifier (case-independence)' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '(?i-x)', note => 'Negated modifier (extended syntax)' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); token( '(?^i)', note => 'Re-apply defaults, plus case-independence' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '(?a)', note => 'Embedded /a' ); m_call( perl_version_introduced => '5.013009', note => 'perl5139delta' ); m_call( perl_version_removed => undef ); token( '(?d)', note => 'Embedded /d' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '(?l)', note => 'Embedded /l' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '(?u)', note => 'Embedded /u' ); m_call( perl_version_introduced => '5.013006', note => 'perl5136delta' ); m_call( perl_version_removed => undef ); token( '(?aa)', note => 'Embedded /aa' ); m_call( perl_version_introduced => '5.013010', note => 'perl51310delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::NoOp', note => 'Does nothing' ); token( '\\N{}', note => 'Unicode character with empty name' ); m_call( perl_version_introduced => MINIMUM_PERL, note => 'perl5238delta (!)' ); m_call( perl_version_removed => '5.027001', note => 'perl5271delta' ); klass( 'PPIx::Regexp::Token::Operator', note => 'Operator' ); token( '|', note => 'Alternation (outside character class)' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '^', cookie => COOKIE_CLASS, note => 'Character class inversion' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '-', cookie => COOKIE_REGEX_SET, note => 'Character range (inside character class)' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Quantifier', note => 'Quantifier' ); token( '*', note => 'Zero or more' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '+', note => 'One or more' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '?', note => 'Zero or one' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); # TODO( the quantifier {m,n} gets covered, if at all, under # PPIx::Regexp::Token::Structure. klass( 'PPIx::Regexp::Token::Recursion', note => 'Recursion' ); token( '(?1)', note => 'Recurse to first capture' ); m_call( perl_version_introduced => '5.009005' ); # perl595delta m_call( perl_version_removed => undef ); token( '(?+1)', note => 'Recurse to next capture' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(?-1)', note => 'Recurse to previous capture' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(?R)', note => 'Recurse to beginning of pattern' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(?&foo)', note => 'Recurse to named capture ' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); token( '(?P>foo)', note => 'Recurse to named capture , PCRE/Python syntax' ); m_call( perl_version_introduced => '5.009005' ); m_call( perl_version_removed => undef ); # PPIx::Regexp::Token::Reference( is the parent of # PPIx::Regexp::Token::Backreference, PPIx::Regexp::Token::Condition, # and( PPIX::Regexp::Token::Recursion. It has no separate tests. klass( 'PPIx::Regexp::Token::Structure', report => 0 ); token( '(' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( ')' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '[' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( ']' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( '{' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Structure', note => 'Quantifier' ); token( '}', is_quantifier => 1, note => 'Explicit quantifier', text => '{n} or {n,m}' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Structure', note => 'Perl operator' ); token( 'm', note => 'Match', text => 'm//' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 's', note => 'Substitute', text => 's///' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); token( 'qr', note => 'Regexp constructor', text => 'qr{}' ); m_call( perl_version_introduced => '5.005', note => 'perl5005delta' ); m_call( perl_version_removed => undef ); # TODO( if the quantifier {m,n} gets forms that are only legal for # certain( Perls, things may get sticky, but at the token level '}' is # the( one marked as a quantifier, so here's the starting point. klass( 'PPIx::Regexp::Token::Whitespace', note => 'White space' ); token( ' ', note => 'Not significant under /x' ); m_call( perl_version_introduced => MINIMUM_PERL, note => '5.3.7 perlre' ); m_call( perl_version_removed => undef ); # RT( #91798. The following was implemented prematurely. What happened in # 5.17.9( was not the recognition of non-ASCII spaces, but the # requirement( that they be escaped, so they could be recognized # eventually. # The( non-ASCII white space was finally introduced in 5.21.1. SKIP: { SUFFICIENT_UTF8_SUPPORT_FOR_WEIRD_DELIMITERS or skip 'Weird delimiters test requires Perl 5.8.3 or above', 3; # The( following eval is to hide the construct from Perl 5.6, which # does( not understand \N{...}. token( eval q<" \\N{U+0085}">, ## no critic (ProhibitStringyEval) note => 'Non-ASCII space' ); m_call( perl_version_introduced => '5.021001', note => 'perl5179delta' ); m_call( perl_version_removed => undef ); } klass( 'PPIx::Regexp::Token::Structure', note => 'Regex set' ); token( '(?[' ); m_call( perl_version_introduced => '5.017008', note => 'perl5178delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::Modifier', note => 'Non-capturing parens' ); token( 'n' ); m_call( perl_version_introduced => '5.021008', note => 'perl5218delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Script_Run', note => 'All characters must be in same script' ); token( '+script_run:' ); m_call( perl_version_introduced => '5.027008', note => 'perl5278delta' ); m_call( perl_version_removed => '5.027009' ); token( '*script_run:' ); m_call( perl_version_introduced => '5.027009', note => 'perl5279delta' ); m_call( perl_version_removed => undef ); token( '*sr:' ); m_call( perl_version_introduced => '5.027009', note => 'perl5279delta' ); m_call( perl_version_removed => undef ); klass( 'PPIx::Regexp::Token::GroupType::Atomic_Script_Run', note => 'All characters must be in same script, atomic version' ); token( '*atomic_script_run:' ); m_call( perl_version_introduced => '5.027009', note => 'perl5279delta' ); m_call( perl_version_removed => undef ); token( '*asr:' ); m_call( perl_version_introduced => '5.027009', note => 'perl5279delta' ); m_call( perl_version_removed => undef ); { note <<'EOD'; Test thing that can not be done using the version-testing framework because they are too dependent on context. EOD foreach my $test ( [ '(?=f?)', '5.000', undef, 'Variable-length look-ahead' ], [ '(?=f{1,2})', '5.000', undef, 'Variable-length look-ahead' ], [ '(?<=f?)', '5.029009', undef, 'Variable-length look-behind' ], [ '(?<=f{1,2})', '5.029009', undef, 'Variable-length look-behind' ], [ '(?(?=(?=x)x)\K)', '5.009005', undef, '\K not really in look-ahead' ], [ '(?(?=(?=x)x\K))', '5.009005', '5.031003', '\K nested one deep' ], [ '(?(?=(?=x\K)x))', '5.009005', '5.031003', '\K nested two deep' ], [ 'x{0,3}', '5.000', undef, 'Quantifier {0,3}' ], [ 'x{,3}', '5.033006', undef, 'Quantifier {,3}' ], [ 'x{ 0 , 3 }', '5.033006', undef, 'Quantifier { 0 , 3 }' ], ) { my $re = "/$test->[0]/"; my $pre = PPIx::Regexp->new( $re ); is $pre->perl_version_introduced(), $test->[1], "$test->[3] $re introduced in $test->[1]"; is $pre->perl_version_removed(), $test->[2], "$test->[3] $re @{[ defined $test->[2] ? qq|removed in $test->[2]| : 'never removed' ]}"; } } finis(); my $context; my @report_info; BEGIN { $context = {}; } sub klass { my ( $class, %args ) = @_; $args{class} = $class; $context = undef; my $title = "require $class"; if ( eval "require $class; 1" ) { $context->{class} = \%args; $REPORT and return; @_ = ( $title ); goto &pass; } else { $REPORT and die $title; @_ = ( "$title: $@" ); goto &fail; } } sub _dor { # Because we do not require 5.010. my ( @args ) = @_; foreach my $arg ( @args ) { defined $arg and return $arg; } return undef; # Yes, I want this in array context. } { my $csv; sub _report { my ( @args ) = @_; if ( ! $csv ) { require Text::CSV; $csv = Text::CSV->new(); $csv->combine( 'Kind', 'Token', 'Descr', 'Introduced', 'Ref', 'Removed', 'Ref' ) or die 'Invalid CSV input: ', $csv->error_input(); print $csv->string(), "\n"; } $csv->combine( @args ) or die 'Invalid CSV input: ', $csv->error_input(); print $csv->string(), "\n"; return; } } sub finis { $REPORT or goto &done_testing; foreach my $item ( @report_info ) { my @data = ( _dor( $item->{class}{note}, $item->{class}{class} ), _dor( $item->{token}{text}, $item->{token}{content} ), _dor( $item->{token}{note}, '' ), ); foreach my $method ( qw{ perl_version_introduced perl_version_removed } ) { if ( $item->{$method} ) { push @data, _dor( $item->{$method}{got}, '' ), _dor( $item->{$method}{note}, '' ); } else { push @data, '', ''; } } while ( @data && '' eq $data[-1] ) { pop @data; } _report( @data ); } return; } { my %annotate; BEGIN { %annotate = map { $_ => 1 } qw{ perl_version_introduced perl_version_removed }; } sub m_call { my ( $method, @args ) = @_; my ( %info, $kind, $want ); if ( $annotate{$method} ) { $kind = $1; $want = shift @args; %info = @args; @args = (); } else { $want = pop @args; } SKIP: { defined $context->{object} or skip 'No object defined', 1; my $argtxt = @args ? ' ' . join( ', ', map { "'$_'" } @args ) . ' ' : ''; my $title; if ( defined $want ) { $title = "$method($argtxt) is '$want'"; } else { $title = "$method($argtxt) is undef"; } my $got; eval { $got = $context->{object}->$method( @args ); 1; } or do { $title .= ": $@"; chomp $title; $REPORT and die $title; @_ = ( $title ); goto &fail; }; $info{got} = $got; $context->{$method} = \%info; $REPORT and return; @_ = ( $got, $want, $title ); goto &is; } } } sub token { my ( $content, %args ) = @_; SKIP: { defined $context->{class} or skip 'No class defined', 1; my $previous = $context->{object} or delete $args{previous}; $context = { class => $context->{class}, token => { content => $content, note => delete $args{note}, }, }; my $text = exists $args{text} ? delete $args{text} : exists $context->{class}{text} ? sprintf $context->{class}{text}, $content : undef; defined $text and $context->{token}{text} = $text; my $report = exists $args{report} ? delete $args{report} : exists $context->{class}{report} ? $context->{class}{report} : 1; $REPORT and $report and push @report_info, $context; my $class = defined $args{class} ? $args{class} : $context->{class}{class}; my $title = sprintf 'Instantiate %s with %s', $class, defined $content ? "'$content'" : '()'; $args{previous} and $title .= sprintf q< preceded by '%s'>, $previous->content(); if ( eval { my $tokenizer = PPIx::Regexp::Tokenizer->new( $content || '', ); if ( my $code = $class->can( '__make_group_type_matcher' ) ) { foreach my $matcher ( @{ $code->( $class )->{''} } ) { $tokenizer->find_regexp( $matcher ) and last; } } if ( my $cookie = delete $args{cookie} ) { $tokenizer->cookie( $cookie => sub { 1 } ); } my $obj = $class->__new( defined $content ? $content : (), $class->isa( 'PPIx::Regexp::Token' ) ? ( tokenizer => $tokenizer ) : (), ); if ( delete $args{previous} ) { require PPIx::Regexp::Node; $context->{parent} = PPIx::Regexp::Node->__new( $previous, $obj ); } if ( my $main = delete $args{main} ) { if ( HASH_REF eq ref $main ) { $main = { %{ $main } }; # Shallow clone } else { $main = { start => $main, finish => $main, }; } foreach my $key ( qw{ start finish } ) { blessed( $main->{$key} ) and next; require PPIx::Regexp::Token::Literal; $main->{$key} = PPIx::Regexp::Token::Literal->__new( $main->{$key} ); } my $class = delete $main->{class} || 'PPIx::Regexp::Structure::Regexp'; eval "require $class; 1" or die "Failed to require $class: $@"; $context->{main} = $class->__new( $main, $obj ); } $context->{object} = $obj; } ) { while ( my ( $name, $val ) = each %args ) { $context->{object}{$name} = $val; } $REPORT and return; @_ = ( $title ); goto &pass; } else { $title .= ": $@"; chomp $title; $REPORT and die $title; @_ = ( $title ); goto &fail; } } } 1; __END__ # ex: set textwidth=72 : PPIx-Regexp-0.082/t/xplain.t000444000765000024 6276614151156043 14777 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use lib qw{ inc }; use My::Module::Test; use Test::More 0.88; # Because of done_testing(); note 'PPIx::Regexp::Node::Range'; parse( '/[a-z]/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0 ); klass( 'PPIx::Regexp::Node::Range' ); xplain( q ); note 'PPIx::Regexp::Token::Assertion'; parse( '/^\\A\\B\\B{gcb}\\B{g}\\B{sb}\\B{wb}\\G\\K\\Z\\b\\b{gcb}\\b{g}\\b{sb}\\b{wb}\B{lb}\b{lb}\\z$/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Assertion' ); value( explain => [], 'Assert position is at beginning of string or after newline' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at beginning of string' ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is not at word/nonword boundary' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is not at grapheme cluster boundary' ); choose( child => 1, child => 4 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is not at grapheme cluster boundary' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is not at sentence boundary' ); choose( child => 1, child => 6 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is not at word boundary' ); choose( child => 1, child => 7 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at pos()' ); choose( child => 1, child => 8 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'In s///, keep everything before the \\K' ); choose( child => 1, child => 9 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at end of string, or newline before end' ); choose( child => 1, child => 10 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at word/nonword boundary' ); choose( child => 1, child => 11 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at grapheme cluster boundary' ); choose( child => 1, child => 12 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at grapheme cluster boundary' ); choose( child => 1, child => 13 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at sentence boundary' ); choose( child => 1, child => 14 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at word boundary' ); choose( child => 1, child => 15 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is not at line boundary' ); choose( child => 1, child => 16 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at line boundary' ); choose( child => 1, child => 17 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at end of string' ); choose( child => 1, child => 18 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'Assert position is at end of string or newline' ); note 'PPIx::Regexp::Token::Backreference'; parse( '/(?x)\\1\\g-1\\g{foo}/' ); value( failures => [], 0 ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Backreference' ); xplain( 'Back reference to capture group 1' ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::Backreference' ); xplain( 'Back reference to 1st previous capture group (1 in this regexp)' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::Backreference' ); xplain( q ); note 'PPIx::Regexp::Token::Backtrack'; parse( '/(*ACCEPT)(*COMMIT)(*FAIL)(*MARK:foo)(*PRUNE:bar)(*SKIP:baz)(*THEN:fee)(*:fie)(*F:foe)/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Causes match to succeed at the point of the (*ACCEPT)' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Causes match failure when backtracked into on failure' ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Always fails, forcing backtrack' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Name branches of alternation, target for (*SKIP)' ); choose( child => 1, child => 4 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Prevent backtracking past here on failure' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Like (*PRUNE) but also discards match to this point' ); choose( child => 1, child => 6 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Force next alternation on failure' ); choose( child => 1, child => 7 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Name branches of alternation, target for (*SKIP)' ); choose( child => 1, child => 8 ); klass( 'PPIx::Regexp::Token::Backtrack' ); xplain( 'Always fails, forcing backtrack' ); note 'PPIx::Regexp::Token::CharClass::POSIX -- asserted'; parse( '/[[:alnum:][:alpha:][:ascii:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:word:][:xdigit:]]/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any alphanumeric character' ); choose( child => 1, child => 0, child => 1 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any alphabetic' ); choose( child => 1, child => 0, child => 2 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any character in the ASCII character set' ); choose( child => 1, child => 0, child => 3 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'A GNU extension, equal to a space or a horizontal tab ("\\t")' ); choose( child => 1, child => 0, child => 4 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any control character' ); choose( child => 1, child => 0, child => 5 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any decimal digit' ); choose( child => 1, child => 0, child => 6 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any non-space printable character' ); choose( child => 1, child => 0, child => 7 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any lowercase character' ); choose( child => 1, child => 0, child => 8 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any printable character' ); choose( child => 1, child => 0, child => 9 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any graphical character excluding "word" characters' ); choose( child => 1, child => 0, child => 10 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any whitespace character' ); choose( child => 1, child => 0, child => 11 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any uppercase character' ); choose( child => 1, child => 0, child => 12 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'A Perl extension, equivalent to "\\w"' ); choose( child => 1, child => 0, child => 13 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Any hexadecimal digit' ); note 'PPIx::Regexp::Token::CharClass::POSIX -- negated'; parse( '/[[:^alnum:][:^alpha:][:^ascii:][:^blank:][:^cntrl:][:^digit:][:^graph:][:^lower:][:^print:][:^punct:][:^space:][:^upper:][:^word:][:^xdigit:]]/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but an alphanumeric character' ); choose( child => 1, child => 0, child => 1 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but an alphabetic' ); choose( child => 1, child => 0, child => 2 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a character in the ASCII character set' ); choose( child => 1, child => 0, child => 3 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'A GNU extension, anything but a space or a horizontal tab ("\\t")' ); choose( child => 1, child => 0, child => 4 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a control character' ); choose( child => 1, child => 0, child => 5 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a decimal digit' ); choose( child => 1, child => 0, child => 6 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a non-space printable character' ); choose( child => 1, child => 0, child => 7 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a lowercase character' ); choose( child => 1, child => 0, child => 8 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a printable character' ); choose( child => 1, child => 0, child => 9 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a graphical character excluding "word" characters' ); choose( child => 1, child => 0, child => 10 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a whitespace character' ); choose( child => 1, child => 0, child => 11 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but an uppercase character' ); choose( child => 1, child => 0, child => 12 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'A Perl extension, equivalent to "\\W"' ); choose( child => 1, child => 0, child => 13 ); klass( 'PPIx::Regexp::Token::CharClass::POSIX' ); xplain( 'Anything but a hexadecimal digit' ); note 'PPIx::Regexp::Token::CharClass::Simple'; parse( '/.\\C\\D\\H\\N\\R\\S\\V\\W\\X\\d\\h\\s\\v\\w\\P{Upper}\\p{Upper}\\p{Script=<\A(?:Latin|Greek)\z>}/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match any character' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a single octet (removed in 5.23.0)' ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match any character but a decimal digit' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a non-horizontal-white-space character' ); choose( child => 1, child => 4 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match any character but a new-line character' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a generic new-line character' ); choose( child => 1, child => 6 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match non-white-space character' ); choose( child => 1, child => 7 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a non-vertical-white-space character' ); choose( child => 1, child => 8 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match non-word character' ); choose( child => 1, child => 9 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a Unicode extended grapheme cluster' ); choose( child => 1, child => 10 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match decimal digit' ); choose( child => 1, child => 11 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a horizontal-white-space character' ); choose( child => 1, child => 12 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match white-space character' ); choose( child => 1, child => 13 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match a vertical-white-space character' ); choose( child => 1, child => 14 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match word character' ); choose( child => 1, child => 15 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match character without Unicode or custom property \'Upper\'' ); choose( child => 1, child => 16 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match character with Unicode or custom property \'Upper\'' ); choose( child => 1, child => 17 ); klass( 'PPIx::Regexp::Token::CharClass::Simple' ); xplain( 'Match character with Unicode wildcard property \'Script=<\A(?:Latin|Greek)\z>\'' ); note 'PPIx::Regexp::Token::Code'; parse( '/(?{foo()})/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0 ); klass( 'PPIx::Regexp::Token::Code' ); xplain( 'Perl expression' ); note 'PPIx::Regexp::Token::Comment'; parse( '/(?#foo)/' ); value( failures => [], 0 ); choose( child => 1, start => 1 ); klass( 'PPIx::Regexp::Token::Comment' ); xplain( 'Comment' ); note 'PPIx::Regexp::Token::Condition'; parse( '/(?(DEFINE))(?(R))(?(1))(?())(?(R1))(?(R&foo))/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0 ); klass( 'PPIx::Regexp::Token::Condition' ); xplain( 'Define a group to be recursed into' ); choose( child => 1, child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Condition' ); xplain( 'True if recursing' ); choose( child => 1, child => 2, child => 0 ); klass( 'PPIx::Regexp::Token::Condition' ); xplain( 'True if capture group 1 matched' ); choose( child => 1, child => 3, child => 0 ); klass( 'PPIx::Regexp::Token::Condition' ); xplain( 'True if capture group \'foo\' matched' ); choose( child => 1, child => 4, child => 0 ); klass( 'PPIx::Regexp::Token::Condition' ); xplain( 'True if recursing directly inside capture group 1' ); choose( child => 1, child => 5, child => 0 ); klass( 'PPIx::Regexp::Token::Condition' ); xplain( 'True if recursing directly inside capture group \'foo\'' ); note 'PPIx::Regexp::Token::Control'; parse( '/\\E\\F\\L\\Q\\U\\l\\u/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'End of interpolation control' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'Fold case until \\E' ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'Lowercase until \\E' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'Quote metacharacters until \\E' ); choose( child => 1, child => 4 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'Uppercase until \\E' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'Lowercase next character' ); choose( child => 1, child => 6 ); klass( 'PPIx::Regexp::Token::Control' ); xplain( 'Uppercase next character' ); note 'PPIx::Regexp::Token::Delimiter'; parse( '//' ); value( failures => [], 0 ); choose( child => 1, start => 0 ); klass( 'PPIx::Regexp::Token::Delimiter' ); xplain( 'Regular expression or replacement string delimiter' ); choose( child => 1, finish => 0 ); klass( 'PPIx::Regexp::Token::Delimiter' ); xplain( 'Regular expression or replacement string delimiter' ); note 'PPIx::Regexp::Token::Greediness'; parse( '/x*?y*+/' ); value( failures => [], 0 ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::Greediness' ); xplain( 'match shortest string first' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Token::Greediness' ); xplain( 'match longest string and give nothing back' ); note 'PPIx::Regexp::Token::GroupType::Assertion'; parse( '/(?!x)(? [], 0 ); choose( child => 1, child => 0, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Assertion' ); xplain( 'Negative look-ahead assertion' ); choose( child => 1, child => 1, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Assertion' ); xplain( 'Negative look-behind assertion' ); choose( child => 1, child => 2, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Assertion' ); xplain( 'Positive look-behind assertion' ); choose( child => 1, child => 3, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Assertion' ); xplain( 'Positive look-ahead assertion' ); note 'PPIx::Regexp::Token::GroupType::BranchReset'; parse( '/(?|(foo)|(bar))/' ); value( failures => [], 0 ); choose( child => 1, child => 0, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::BranchReset' ); xplain( 'Re-use capture group numbers' ); note 'PPIx::Regexp::Token::GroupType::Code'; parse( '/(?{foo()})(?p{bar()})(??{baz()})/' ); value( failures => [], 0 ); choose( child => 1, child => 0, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Code' ); xplain( 'Evaluate code. Always matches.' ); choose( child => 1, child => 1, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Code' ); xplain( 'Evaluate code, use as regexp at this point (removed in 5.9.5)' ); choose( child => 1, child => 2, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Code' ); xplain( 'Evaluate code, use as regexp at this point' ); note 'PPIx::Regexp::Token::GroupType::NamedCapture'; parse( '/(?\\d)/' ); value( failures => [], 0 ); choose( child => 1, child => 0, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::NamedCapture' ); xplain( 'Capture match into \'foo\'' ); note 'PPIx::Regexp::Token::GroupType::Subexpression'; parse( '/(?>x)/' ); value( failures => [], 0 ); choose( child => 1, child => 0, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Subexpression' ); xplain( 'Match subexpression without backtracking' ); note 'PPIx::Regexp::Token::GroupType::Switch'; parse( '/(?(1)x|y)/' ); value( failures => [], 0 ); choose( child => 1, child => 0, type => 0 ); klass( 'PPIx::Regexp::Token::GroupType::Switch' ); xplain( 'Match one of the following \'|\'-delimited alternatives' ); note 'PPIx::Regexp::Token::Literal'; parse( '/x/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Literal' ); xplain( 'Literal character' ); note 'PPIx::Regexp::Token::Modifier'; parse( '/(foo(?u-n:(bar)))/smxna' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 3, type => 0 ); klass( 'PPIx::Regexp::Token::Modifier' ); xplain( 'u: match using Unicode semantics; -n: parentheses capture' ); choose( child => 2 ); klass( 'PPIx::Regexp::Token::Modifier' ); xplain( 'a: restrict non-Unicode classes to ASCII; m: ^ and $ match within string; n: parentheses do not capture; s: . can match newline; x: ignore whitespace and comments' ); note 'PPIx::Regexp::Token::Unknown since 5.28; previously ::NoOp'; parse ( '/\\N{}/' ); value ( failures => [], 1 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Unknown' ); xplain ( 'Empty Unicode character name' ); note 'PPIx::Regexp::Token::Operator'; parse( '/(?[(\\w-[[:lower:]])|\\p{Greek}])|[^a-z]/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0, child => 1 ); klass( 'PPIx::Regexp::Token::Operator' ); xplain( 'Subtraction operator' ); choose( child => 1, child => 0, child => 1 ); klass( 'PPIx::Regexp::Token::Operator' ); xplain( 'Union operator' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Operator' ); xplain( 'Alternation operator' ); choose( child => 1, child => 2, type => 0 ); klass( 'PPIx::Regexp::Token::Operator' ); xplain( 'Inversion operator' ); choose( child => 1, child => 2, child => 0, child => 1 ); klass( 'PPIx::Regexp::Token::Operator' ); xplain( 'Range operator' ); note 'PPIx::Regexp::Token::Quantifier'; parse( '/a*b+c?/' ); value( failures => [], 0 ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Quantifier' ); xplain( 'match zero or more times' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::Quantifier' ); xplain( 'match one or more times' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Token::Quantifier' ); xplain( 'match zero or one time' ); note 'PPIx::Regexp::Token::Structure'; parse( '/(?[(\\w-[0-9])]){1,3}/' ); value( failures => [], 0 ); choose( child => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'Match regexp' ); choose( child => 1, start => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'Regular expression or replacement string delimiter' ); choose( child => 1, child => 0, start => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'Extended character class' ); choose( child => 1, child => 0, child => 0, start => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'Capture or grouping' ); choose( child => 1, child => 0, child => 0, child => 2, start => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'Character class' ); choose( child => 1, child => 0, child => 0, child => 2, finish => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'End character class' ); choose( child => 1, child => 0, child => 0, finish => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'End capture or grouping' ); choose( child => 1, child => 0, finish => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'End extended character class' ); choose( child => 1, finish => 0 ); klass( 'PPIx::Regexp::Token::Structure' ); xplain( 'Regular expression or replacement string delimiter' ); note 'PPIx::Regexp::Token::Recursion'; parse( '/(?x(?1))(?R)(?0)(?&z)/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 1 ); klass( 'PPIx::Regexp::Token::Recursion' ); xplain( 'Recurse into capture group 1' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Recursion' ); xplain( 'Recurse to beginning of regular expression' ); choose( child => 1, child => 2 ); klass( 'PPIx::Regexp::Token::Recursion' ); xplain( 'Recurse to beginning of regular expression' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Token::Recursion' ); xplain( 'Recurse into capture group \'z\'' ); note 'PPIx::Regexp::Token::Unmatched'; parse( '/)/' ); value( failures => [], 1 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Unmatched' ); xplain( 'Unmatched token' ); note 'PPIx::Regexp::Token::Whitespace'; parse( 's{ (?[ \\d])} {x}x' ); value( failures => [], 0 ); choose( child => 1, start => 1 ); klass( 'PPIx::Regexp::Token::Whitespace' ); xplain( 'Not significant under /x' ); choose( child => 1, child => 0, start => 1 ); klass( 'PPIx::Regexp::Token::Whitespace' ); xplain( 'Not significant in extended character class' ); choose( child => 2 ); klass( 'PPIx::Regexp::Token::Whitespace' ); xplain( 'Not significant' ); note 'PPIx::Regexp::Structure::Capture'; parse( '/(\\d+)(?\\w+)/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Structure::Capture' ); xplain( 'Capture group number 1' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Structure::Capture' ); xplain( 'Named capture group \'foo\' (number 2)' ); note 'PPIx::Regexp::Structure::Quantifier'; parse( '/x{1,4}y{2,}z{3}w{$foo}/' ); value( failures => [], 0 ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Structure::Quantifier' ); xplain( 'match 1 to 4 times' ); choose( child => 1, child => 3 ); klass( 'PPIx::Regexp::Structure::Quantifier' ); xplain( 'match 2 or more times' ); choose( child => 1, child => 5 ); klass( 'PPIx::Regexp::Structure::Quantifier' ); xplain( 'match exactly 3 times' ); choose( child => 1, child => 7 ); klass( 'PPIx::Regexp::Structure::Quantifier' ); xplain( 'match $foo times' ); note 'PPIx::Regexp::Structure::Regexp'; parse( '/x/' ); value( failures => [], 0 ); choose( child => 1 ); klass( 'PPIx::Regexp::Structure::Regexp' ); xplain( 'Regular expression' ); note 'PPIx::Regexp::Structure::Replacement'; parse( 's/x/y/' ); value( failures => [], 0 ); choose( child => 2 ); klass( 'PPIx::Regexp::Structure::Replacement' ); xplain( 'Replacement string or expression' ); note 'PPIx::Regexp'; parse( '/x/' ); value( failures => [], 0 ); choose(); klass( 'PPIx::Regexp' ); xplain( undef ); note 'Caret'; parse( '/(?^)(x)/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Modifier' ); xplain( 'd: match using default semantics; -i: do case-sensitive matching; -m: ^ and $ match only at ends of string; -s: . can not match newline; -x: regard whitespace as literal' ); parse( '/(?^)(x)/n' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Token::Modifier' ); xplain( 'd: match using default semantics; -i: do case-sensitive matching; -m: ^ and $ match only at ends of string; -n: parentheses capture; -s: . can not match newline; -x: regard whitespace as literal' ); note 'Capture vs grouping'; parse( '/(x)/' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Structure::Capture' ); xplain( 'Capture group number 1' ); parse( '/(x)/n' ); value( failures => [], 0 ); choose( child => 1, child => 0 ); klass( 'PPIx::Regexp::Structure' ); xplain( 'Grouping' ); note 'Retraction of \\K inside look-around'; parse( '/(?=\\K)\K/' ); value( failures => [], 0 ); choose( child => 1, child => 0, child => 0 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'In s///, keep everything before the \K; retracted inside look-around assertion' ); value( perl_version_removed => [], '5.031003' ); choose( child => 1, child => 1 ); klass( 'PPIx::Regexp::Token::Assertion' ); xplain( 'In s///, keep everything before the \K' ); value( perl_version_removed => [], undef ); done_testing; sub xplain { splice @_, 0, 0, explain => []; goto &value; } 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt000755000765000024 014151156043 13311 5ustar00tomstaff000000000000PPIx-Regexp-0.082/xt/author000755000765000024 014151156043 14613 5ustar00tomstaff000000000000PPIx-Regexp-0.082/xt/author/carp_not.t000444000765000024 135614151156043 16747 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use ExtUtils::Manifest qw{ maniread }; use PPIx::Regexp::Constant qw{ @CARP_NOT }; use Test::More 0.88; # Because of done_testing(); my @modules; foreach my $fn ( sort keys %{ maniread() } ) { local $_ = $fn; s< \A lib/ ><>smx or next; s< [.] pm \z ><>smx or next; s< / ><::>smxg; push @modules, $_; local $/ = undef; open my $fh, '<:encoding(utf-8)', $fn or do { fail "Unable to open $fn: $!"; next; }; my $content = <$fh>; close $fh; ok $content =~ m/ \@CARP_NOT \b /smx, "$_ assigns \@CARP_NOT"; } is_deeply \@CARP_NOT, \@modules, 'Ensure that @PPIx::Regexp::Constant::CARP_NOT is correct'; done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/changes.t000444000765000024 60214151156043 16523 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use Test::More 0.88; # Because of done_testing(); BEGIN { eval { require Test::CPAN::Changes; Test::CPAN::Changes->import(); 1; } or do { plan skip_all => 'Unable to load Test::CPAN::Changes'; exit; }; } changes_file_ok( Changes => { next_token => 'next_release' } ); done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/critic.t000444000765000024 53614151156043 16376 0ustar00tomstaff000000000000package main; use strict; use warnings; use File::Spec; use Test::More 0.88; BEGIN { eval { require Test::Perl::Critic; Test::Perl::Critic->import( -profile => File::Spec->catfile(qw{xt author perlcriticrc}) ); 1; } or do { plan skip_all => 'Test::Perl::Critic required to criticize code.'; exit; }; } all_critic_ok(); 1; PPIx-Regexp-0.082/xt/author/executable.t000444000765000024 111414151156043 17253 0ustar00tomstaff000000000000package main; use strict; use warnings; use ExtUtils::Manifest qw{maniread}; use Test::More 0.88; my $manifest = maniread(); foreach ( sort keys %{ $manifest } ) { m{ \A bin / }smx and next; m{ \A eg / }smx and next; m{ \A tools / }smx and next; ok ! is_executable(), "$_ should not be executable"; } done_testing; sub is_executable { my @stat = stat $_; $stat[2] & oct(111) and return 1; open my $fh, '<', $_ or die "Unable to open $_: $!\n"; local $_ = <$fh>; close $fh; return m{ \A [#]! .* perl }smx; } 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/kwalitee.t000444000765000024 53214151156043 16722 0ustar00tomstaff000000000000package main; use 5.006002; use strict; use warnings; use Test::More 0.88; eval { require Test::Kwalitee; Test::Kwalitee->import(); -f 'Debian_CPANTS.txt' # Don't know what this is, and unlink 'Debian_CPANTS.txt'; # but _I_ didn't order it. 1; } or plan skip_all => 'Test::Kwalitee not found'; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/manifest.t000444000765000024 116214151156043 16743 0ustar00tomstaff000000000000package main; use strict; use warnings; BEGIN { eval { require Test::More; Test::More->VERSION( 0.88 ); # Because of done_testing() Test::More->import(); 1; } or do { print <import( qw{ manicheck filecheck } ); 1; } or do { plan( skip_all => "ExtUtils::Manifest required" ); exit; }; } plan( tests => 2 ); is( join( ' ', manicheck() ), '', 'Missing files per manifest' ); is( join( ' ', filecheck() ), '', 'Files not in MANIFEST or MANIFEST.SKIP' ); 1; PPIx-Regexp-0.082/xt/author/minimum_perl.t000444000765000024 262614151156043 17640 0ustar00tomstaff000000000000package main; use 5.008; use strict; use warnings; use Test::More 0.88; # Because of done_testing(); eval { require ExtUtils::Manifest; 1; } or plan skip_all => 'Unable to load ExtUtils::Manifest'; eval { require Perl::MinimumVersion; 1; } or plan skip_all => 'Unable to load Perl::MinimumVersion'; eval { require version; 1; } or plan skip_all => 'Unable to load version'; use lib qw{ inc }; use My::Module::Meta; my $min_perl = My::Module::Meta->requires_perl(); my $min_perl_vers = version->parse( $min_perl ); my $manifest = ExtUtils::Manifest::maniread(); foreach my $fn ( sort keys %{ $manifest } ) { $fn =~ m{ \A xt/ }smx and next; is_perl( $fn ) or next; my $doc = Perl::MinimumVersion->new( $fn ); cmp_ok $doc->minimum_version(), 'le', $min_perl, "$fn works under Perl $min_perl"; my $ppi_doc = $doc->Document(); foreach my $inc ( @{ $ppi_doc->find( 'PPI::Statement::Include' ) || [] } ) { my $vers = $inc->version() or next; cmp_ok( version->parse( $vers ), '==', $min_perl_vers, "$fn has use $min_perl, rather than some other version" ); last; } } done_testing; sub is_perl { my ( $fn ) = @_; $fn =~ m/ [.] (?: pm | t | pod | (?i: pl ) ) \z /smx and return 1; -f $fn and -T _ or return 0; open my $fh, '<', $fn or return 0; local $_ = <$fh>; close $fh; return m/ perl /smx; } 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/perlcriticrc000444000765000024 370414151156043 17364 0ustar00tomstaff000000000000severity = stern theme = core [Perl::Critic::Policy::BuiltinFunctions::ProhibitStringyEval] allow_includes = 1 [Perl::Critic::Policy::Documentation::PodSpelling] spell_command = aspell list [Perl::Critic::Policy::ErrorHandling::RequireCheckingReturnValueOfEval] # The default is 3 ('harsh'), but I think this is more severe than that. severity = stern [Perl::Critic::Policy::InputOutput::ProhibitInteractiveTest] # Perl::Critic and Perl Best Practices prefer the IO::Interactive # is_interactive() subroutine to -T STDIN. But that assumes that # ARGV is used for input, and that you don't want to be interactive # if output goes to a pipe. I do not want these assumptions, but # rather than disable them in the code I am simply assigning them # a severity slightly higher than I currently use. severity = harsh [Perl::Critic::Policy::InputOutput::RequireCheckedOpen] # For some reason the default is 3 ('harsh'). But IM(NS)HO this # kind of thing should be a 5. So: severity = gentle [Perl::Critic::Policy::Subroutines::ProhibitUnusedPrivateSubroutines] severity = stern private_name_regex = _(?!_)\w+ [Perl::Critic::Policy::Subroutines::RequireArgUnpacking] short_subroutine_statements = 3 [Perl::Critic::Policy::Subroutines::RequireFinalReturn] terminal_funcs = CORE::exit [Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict] allow = refs [Perl::Critic::Policy::TestingAndDebugging::ProhibitNoWarnings] allow = exiting once substr uninitialized [Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma] # Perl::Critic and Perl Best Practices do not like the 'constant' # pragma because it does not interpolate. It really does, the # syntax is just different. Rather than disable the things in the # source, I'm just assigning them a severity slightly greater than # I customarily use. severity = harsh [Perl::Critic::Policy::Variables::ProhibitUnusedVarsStricter] add_themes = core allow_unused_subroutine_arguments = 0 severity = stern PPIx-Regexp-0.082/xt/author/pod.t000444000765000024 51614151156043 15701 0ustar00tomstaff000000000000package main; use strict; use warnings; use Test::More 0.88; BEGIN { eval { require Test::Pod; Test::Pod->VERSION (1.00); Test::Pod->import(); 1; } or do { print <VERSION(1.00); Test::Pod::Coverage->import(); 1; } or do { print < [ qr{^[[:upper:]\d_]+$}, ], coverage_class => 'Pod::Coverage::CountParents' } ); } done_testing; 1; PPIx-Regexp-0.082/xt/author/pod_links.t000444000765000024 74114151156043 17101 0ustar00tomstaff000000000000package main; use 5.008; use strict; use warnings; use Test::More 0.88; # Because of done_testing(); BEGIN { local $@ = undef; eval { require Test::Pod::LinkCheck::Lite; Test::Pod::LinkCheck::Lite->import( ':const' ); 1; } or plan skip_all => 'Unable to load Test::Pod::LinkCheck::Lite'; } Test::Pod::LinkCheck::Lite->new( prohibit_redirect => ALLOW_REDIRECT_TO_INDEX, )->all_pod_files_ok( qw{ blib eg }, ); done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/pod_spelling.t000444000765000024 133614151156043 17617 0ustar00tomstaff000000000000package main; use strict; use warnings; BEGIN { eval { require Test::Spelling; Test::Spelling->import(); 1; } or do { print "1..0 # skip Test::Spelling not available.\n"; exit; }; } add_stopwords( ); all_pod_files_spelling_ok(); 1; __DATA__ Autoconf CharClass charnames errstr expl graphemes hocery indices infeasible instantiation jb kluginess lexed lexes merchantability nav navigational perlrecharclass perluniprops POSIX postderef postfix PPI ppi PPI's reblessed reblesses remov repl schild schildren scontent subclasses subscripted TODO tokenization Tokenize tokenize tokenized tokenizer's TOKENIZERS tokenizers tokenizes tokenizing trigraphs Un unicode unforseen unterminated UTF version's Wyant XS PPIx-Regexp-0.082/xt/author/prereq.t000444000765000024 62314151156043 16414 0ustar00tomstaff000000000000package main; use strict; use warnings; use Test::More 0.88; # Because of done_testing(); eval { require Test::Prereq::Meta; 1; } or plan skip_all => 'Test::Prereq::Meta not available'; my $tpm = Test::Prereq::Meta->new( accept => [ qw{ Text::CSV } ], uses => [ qw{ Task::Weaken } ], ); $tpm->all_prereq_ok(); $tpm->all_prereqs_used(); done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/unicode_short_charclass.t000444000765000024 252114151156043 22025 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use PPIx::Regexp; use Test::More 0.88; # Because of done_testing(); note <<'EOD'; Scraping perluniprops seems fragile because it is, but I can not think of better way to find out what all the single-character property names are. If this breaks too often I may end up going to just matching [[:upper:]] or something like that. Note to self: the relevant regular expression is in PPIx::Regexp::Token::CharClass::Simple method __PPIX_TOKENIZER__regexp() EOD my %prop; foreach ( `perldoc -oText perluniprops` ) { m/ \\p [{] ( . ) [}] .*? \\p [{] ( .{2,}? ) [}] /smx or next; $prop{$1} ||= $2; } is_deeply \%prop, { C => 'Other', L => 'Letter', M => 'Mark', N => 'Number', P => 'Punct', S => 'Symbol', Z => 'Separator', }, 'All single-character properties are accounted for'; foreach my $letter ( sort keys %prop ) { my $token = "\\p$letter"; my $text = "/$token/"; my $pre = PPIx::Regexp->new( $text ); my $re = $pre->regular_expression(); my @kids = $re->children(); cmp_ok scalar( @kids ), '==', 1, "'$text' parsed to a single token"; cmp_ok $kids[0]->content(), 'eq', $token, "'$text' contains token $token"; isa_ok $kids[0], 'PPIx::Regexp::Token::CharClass::Simple', "Token $token"; } done_testing; 1; # ex: set textwidth=72 : PPIx-Regexp-0.082/xt/author/unit_left_curly.t000444000765000024 371514151156043 20352 0ustar00tomstaff000000000000package main; use 5.006; use strict; use warnings; use Test::More 0.88; # Because of done_testing(); # Mung with manifest constants, since at the time this was written they # were undefined. use PPIx::Regexp::Constant(); # Local constants describing the current removal plans for various # constructs involving un-escaped left curlys. Everything should be done # in terms of these because we seem to be shooting at a moving target on # this issue. use constant REMOVE_NEVER => undef; use constant REMOVE_PHASE_2 => '5.030'; use constant REMOVE_PHASE_3 => '5.032'; { no warnings qw{ redefine }; # These get hammered over what was loaded above. At least under # 5.26.1, we do NOT want an explicit return(), because it seems to # prevent the constant from being inlined. MAYBE we don't care about # that during testing, but on the other hand, the testing # environment should be as much like the live environment as # possible. sub PPIx::Regexp::Constant::LITERAL_LEFT_CURLY_REMOVED_PHASE_2 () { REMOVE_PHASE_2 } # As of 2018-06-08 this is the plan sub PPIx::Regexp::Constant::LITERAL_LEFT_CURLY_REMOVED_PHASE_3 () { REMOVE_PHASE_3 } # As of 2018-06-08 this is the plan } use lib qw{ inc }; use My::Module::Test; parse ( '/x{/' ); # } value ( failures => [], 0 ); choose ( child => 1, child => 1 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); # } value ( perl_version_removed => [], REMOVE_PHASE_2 ); # THIS IS THE POINT parse ( '/ { /x' ); # } value ( failures => [], 0 ); choose ( child => 1, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); # } value ( perl_version_removed => [], REMOVE_NEVER ); # THIS IS THE POINT parse ( '/ ( { ) /x' ); # } value ( failures => [], 0 ); choose ( child => 1, child => 0, child => 0 ); klass ( 'PPIx::Regexp::Token::Literal' ); content ( '{' ); # } value ( perl_version_removed => [], REMOVE_PHASE_3 ); # THIS IS THE POINT done_testing; 1; # ex: set textwidth=72 :