=head1 NAME RegexLab - a Wx Dialog/App for testing Regular Expressions =head1 SYNOPSIS perl -MRegexLab -e RegexLab::App->new()->MainLoop() # or use RegexLab; # or should it be Wx::RegexLab RegexLab::App->new()->MainLoop(); # or even require RegexLab; exec $^X, $INC{RegexLab.pm}; # or the oneliner version (quotes may vary ;) perl -mRegexLab -e"exec $^X, $INC{q{RegexLab.pm}};" # -m is equivalent to use RegexLab(); in case you was wondering =head1 DESCRIPTION Run it as a standalone app, or embed it easily into any wxPerl application, so you can test and devise regular expressions without starting a separate shell. Install YAPE::Regex::Explain to gain insight into what you wrote. Perfect for newbies and experienced users. $Id: RegexLab.pm,v 1.10 2002/12/01 14:02:30 _ Exp $ =cut package RegexLab; use strict; use Wx qw[ :everything ]; use Wx::Event qw[ EVT_BUTTON EVT_CHECKBOX EVT_RIGHT_DOWN EVT_MENU EVT_COMMAND]; use Wx::XRC; use Wx::Html; use base 'Wx::Frame'; use vars qw( $revision $VERSION ); $revision = '$Id: RegexLab.pm,v 1.10 2002/12/01 14:02:30 _ Exp $'; $VERSION = sprintf("%d.%02d", q$Revision: 1.10 $ =~ /: (\d+)\.(\d+)/); =head1 What you need to know =head2 C Creates an instance of C, a subclass of C This is the only function you should call if not running it as an app. You know what, don't. Just always run it as an app =cut sub new { my( $class ) = shift; my $self = $class->SUPER::new( undef, -1, "Regex Lab - you know what it is ;)", [0,0], [350,350], wxDEFAULT_FRAME_STYLE | wxCLIP_CHILDREN, # for easy commenting out ); $self->SetIcon( Wx::GetWxPerlIcon() ); my $rs = Wx::XmlResource->new; $rs->InitAllHandlers(); $rs->Load( 'RegexLab.xrc' ); my( $MainSizer ) = Wx::BoxSizer->new( Wx::wxHORIZONTAL ); $MainSizer->Add( $rs->LoadPanel($self,'regexLabPanel'), 1, wxGROW ); $self->SetSizer( $MainSizer ); $self->SetAutoLayout( 1 ); ## ;) $self->Layout(); ##force layout of the children anew $MainSizer->Fit( $self ); $MainSizer->SetSizeHints( $self ); $self->{regexLabPanel} = $self->XRC('regexLabPanel'); $self->{gCheckbox} = $self->XRC('gCheckbox'); $self->{iCheckbox} = $self->XRC('iCheckbox'); $self->{mCheckbox} = $self->XRC('mCheckbox'); $self->{eCheckbox} = $self->XRC('eCheckbox'); $self->{sCheckbox} = $self->XRC('sCheckbox'); $self->{xCheckbox} = $self->XRC('xCheckbox'); $self->{explainCheckbox} = $self->XRC('explainCheckbox'); $self->{replacementCheckbox} = $self->XRC('replacementCheckbox'); $self->{replacementString} = $self->XRC('replacementString'); $self->{regexString} = $self->XRC('regexString'); $self->{testString} = $self->XRC('testString'); $self->{testButton} = $self->XRC('testButton'); $self->{htmlOut} = $self->XRC('htmlOut'); $self->{replacementString}->SetValue(q[$2-$1ay]); EVT_CHECKBOX( $self, $self->{replacementCheckbox}, \&MatchOrSubstitute ); EVT_BUTTON( $self, $self->{testButton}, \&TestMeRegex ); EVT_RIGHT_DOWN( $self, \&OnAbout ); EVT_RIGHT_DOWN( $self->{regexLabPanel}, \&OnAbout ); EVT_RIGHT_DOWN( $self->{htmlOut}, \&OnAbout ); $self->FlickEmBoxes(); return $self; } sub TestMeRegex { #################### # html color block my $matchcolor = '"#66ff66"'; my $capturecolor = '"#ffff66"'; my $errorcolor = '"#FF6568"'; my $flagcolor = '"#FFABFC"'; my $svarcolor = '"#00D9EB"'; #'"#993399"'; my $explaincolor = '"#00E5FF"'; #'"#993366"'; # end html colors #################### my( $self, $event ) = @_; my( $hOut ) = $self->{htmlOut}; my( $ret ) = ""; my( @Mflags ) = qw( i x m g s ); my( $E ) = $self->{eCheckbox}; # REGEXSTRING INSTRING REPSTRING my $REGEXSTRING = $self->{regexString}->GetValue(); my $INSTRING = $self->{testString}->GetValue(); #use vars qw[ $REPSTRING ]; my $REPSTRING = $self->{replacementString}->GetValue(); my $RegexFlags = ""; use vars qw[ $INSTRING2 $match $REGEX_QR ]; local( $INSTRING2, $match, $REGEX_QR ); unless( $REGEXSTRING ) { # do we have valid pattern? $ret.=qq[
Error in regex text : $@
<\html> ]; $hOut->SetPage( $ret ); return(); }; my %FLAGS=(); my $FLAGS=""; for my $flag ( qw[ g i m s e x ]) { # what flags are we using? if( $self->{$flag."Checkbox"}->GetValue() ) { $FLAGS{$flag}=1; } } $REGEX_QR = eval qq[ qr{$REGEXSTRING}$FLAGS ]; ## ugh, LAME!!!! if(exists $FLAGS{g} ) { delete $FLAGS{g}; $FLAGS = join '', keys %FLAGS if %FLAGS; $FLAGS{g}=1; } else { $FLAGS = join '', keys %FLAGS if %FLAGS; } if( exists $INC{'YAPE/Regex/Explain.pm'} and $self->{explainCheckbox}->GetValue() ) { my $yape_text = YAPE::Regex::Explain->new( $REGEX_QR )->explain; $yape_text =~ s{<}{<}g; $yape_text =~ s{>}{>}g; $ret .= qq[
            $yape_text
            
]; } elsif(keys %FLAGS) { $ret.=qq[ Using modifier(s): ].join(' ',keys %FLAGS ).q[ ]; } if( $INSTRING =~ m{$REGEX_QR} ) { # we match! (i'm quoting boo ;) my @match=(); if ( exists $FLAGS{g} ) { @match = ( $INSTRING =~ m{$REGEX_QR}g ); } $ret.= qq[ Regex text is : $REGEXSTRING Compiled regex is : $REGEX_QR ]; if (exists $FLAGS{g}){ # output global matches in ordered list form @match= map {"'$_'"}@match; $ret .=qq[
Global Matches
    ]; $ret .=join "\n",(map {"
  1. $_
  2. "} @match); } else { # not a global search, but matches may exist if (defined $1){ # print out $1, $2, etc. $ret.= qq(
    Listing matches
    ); ####EVIL, EVIL, EVIL!!!!!! Okay, not so evil, but I have issues w/it. #### I guess i'll have to rewrite this (can't steal everything) no strict qw( refs ); # booga booga! my $ct=1; while (defined ${"$ct"}) { $ret .= qq( \$$ct is : '${"$ct"}' ); $ct++; } } else { print "not found\n"; } } } else { # no match. so sorry. return early. $ret .= qq{No match.}; if ($REGEXSTRING =~ m{\n} and exists $FLAGS{x} ) { $ret .=qq[

    Found a newline in the regex, maybe you meant to enable /x?

    ]; } $hOut->SetPage( $ret.q[<\html>] ); return; } ## Substitution? ## REGEXSTRING INSTRING REPSTRING if( $self->{replacementCheckbox}->GetValue() ) { my $REPLACEMENT = $self->{regexString}->GetValue(); $INSTRING2 = $INSTRING; if( $self->{eCheckbox}->GetValue() ) { if(! index( $INC{'Safe.pm'}, 'Safe.pm', -8) == 12 ) { # really safe my $safe = Safe->new(); $safe->share(qw( $INSTRING2 $REGEX_QR $match )); $safe->trap(qw( :filesys_write :subprocess :ownprocess :dangerous :base_thread goto ) ); $safe->permit(qw( :still_to_be_decided ) ); my $DANGER = $REPSTRING; ## MAKE IT SAFE , cause I use {} as my delimiters $DANGER =~ s/([}{])/\\$1/g; $DANGER = q[ $match = ( $INSTRING2 =~ s{$REGEX_QR}{].$DANGER; $DANGER.= '}e'.$FLAGS.');'; $safe->reval( $DANGER ); if($@){ $ret.=qq[ Error in eval text : $@ <\html> ]; $hOut->SetPage( $ret ); return(); } if(exists $FLAGS{g}) { $ret .=qq[
    1) Substitution global match count : $match
    ]; } } else { ## THE UNSAFE EVAL (YOU CAN exit() and do all kinds of other stuff ## MAKE IT SAFE , cause I use {} as my delimiters $REPSTRING =~ s/([}{])/\\$1/g; my $codefromoutside = q[ $match = ( $INSTRING2 =~ s{$REGEX_QR}{]; $codefromoutside.= $REPSTRING."}"; $codefromoutside.= exists $FLAGS{g} ? 'g' : ''; $codefromoutside.= 'e );'; eval $codefromoutside; if($@){ $ret.=qq[ Error in eval text : $@ <\html> ]; $hOut->SetPage( $ret ); return(); } if (exists $FLAGS{g}) { $ret .=qq[
    2) Substitution global match count : $match
    ]; } } } else { ## IT'S A SIMPLE STRING REPLACEMENT (NOT EVAL, STRING) ## MAKE IT SAFE , cause I use {} as my delimiters $REPSTRING =~ s/([}{])/\\$1/g; my $codefromoutside = q[ $match = ( $INSTRING2 =~ s{$REGEX_QR}{]; $codefromoutside.= $REPSTRING."}"; $codefromoutside.= exists $FLAGS{g} ? 'g' : ''; $codefromoutside.= ');'; eval $codefromoutside; if (exists $FLAGS{g}) { $ret .=qq[
    4) Substitution global match count : $match
    ]; } } } unless ($INSTRING eq $INSTRING2) { $ret.=qq(
    5} Replacement
    Contents of test text changed :
    Was : '$INSTRING'
    Is now : '$INSTRING2'
    ); } ## these don't seem like they'd matter in a global match/search... unless (exists $FLAGS{g}) { $ret .= qq[ \$& is '$&' \$` is '$`' \$' is '$'' \$+ is '$+' ]; } $hOut->SetPage( $ret.q[<\html>] ); } sub FakeACheckAndGetOffFree { my($self, $evt ) = @_; warn "@_"; } sub MatchOrSubstitute { my( $self, $event ) = @_; # my $state = $event->IsChecked() ? 1 : 0 ; ## if $event is one from an accelerator, the box don't get checked, but i took that part out ? my $state = $self->{replacementCheckbox}->GetValue() ? 1 : 0; $self->{replacementString}->Enable( $state ); $self->{replacementCheckbox}->SetValue( $state ); $self->{eCheckbox}->Enable( $state ); } sub FlickEmBoxes { my $self = shift; ## tool tips seem to suck for lables and text controls if( eval q{require Safe}) { warn "Safe is instaled, enabling e modifier"; } else { if( Wx::MessageBox( "You don't have Safe.pm installed, so\n". "it may be unsafe to use the /e modifier\n". "in a substitution.\n". "Do you want to enable the e flag?\n". "(be warned, you can only say yes once)", "WATCHOUT!!! Safe.pm isn't installed.", wxYES_NO, $self, ) == wxYES ){ $INC{'Safe.pm'}++; } else { $self->{'eCheckbox'}->Show(0); $self->{'eCheckbox'}->SetValue(0); } } if( eval {require YAPE::Regex::Explain} ){ warn "YAPE::Regex::Explain is installed, enabling Explain"; $self->{explainCheckbox}->Enable(1); } #the rohnettes opened for the stones Wx::ToolTip::Enable(1); Wx::ToolTip::SetDelay(50); # ms } sub XRC { my($self,$object)=@_; return $self->FindWindow( Wx::XmlResource::GetXRCID( $object ) ); } # display a simple about box sub OnAbout { my( $this, $event ) = @_; my $about = __PACKAGE__." $VERSION\nCreated by PodMaster\n" . "Running on wxPerl $Wx::VERSION"."\n" . wxVERSION_STRING; Wx::MessageBox( $about, "About ".__PACKAGE__, # TITLE wxOK | wxICON_INFORMATION, $this, ); } package RegexLab::App; use strict; use Wx; use base qw(Wx::App); sub OnInit { my $self = shift; my $frame = RegexLab->new(); $self->SetTopWindow($frame); $frame->Show(1); $frame->Refresh(); return 1; } package main; # if this file is invoked directly (not use'd), run the app unless( caller() ) { RegexLab::App->new()->MainLoop(); } __END__ We are not afraid to be on fire. =head1 CAVEATS RegexLab.xrc is required =head1 AUTHOR Originally written by boo_radley of PerlMonks.org fame. Transformed into its this form by PodMaster (same fame), for easy inclusion in other Wx applications, or for standalone use. If you want the original, it's available at http://perlmonks.org/index.pl?node_id=164418 This one is available at http://perlmonks.org/index.pl?node_id=216762 Released under the same terms as perl it self (see perl.com for more info). =head1 TODO +Clean up the code a little, maybe refactor some logic. +Maybe add HTML::Template support (everybody loves it, right? ;) +And perhpas a color-scheme chooser (maybe even stylesheet support) =cut ## note to self ;D ## cvs co -r 1.5 RegexLab