This and other RFCs are available on the web at
  http://dev.perl.org/rfc/

=head1 TITLE

Regex: Support for incremental pattern matching

=head1 VERSION

  Maintainer: Damian Conway <[EMAIL PROTECTED]>
  Date: 11 August 2000
  Last Modified: 24 August 2000
  Version: 2
  Mailing List: [EMAIL PROTECTED]
  Number: 93

=head1 ABSTRACT

This RFC proposes that, in addition to strings, subroutine references may be
bound (with =~ or !~ or implicitly) to a regular expression.

=head1 CHANGES

Version 2 of this RFC redirects discussion of this topic to 
[EMAIL PROTECTED]

=head1 DESCRIPTION

It is proposed that the Perl 6 regular expression engine be extended to
allow a regex to match against an incremental character source, rather than
only against a fixed string.

Specifically, it is proposed that a subroutine reference could be bound
to a regular expression:

        sub {...} =~ /pattern/;

As the regular expression is matched, it would make calls to the subroutine
to request additional characters to match, or (after it has matched) to 
return any unused characters.

When the regex engine requires additional characters to match, the
subroutine would be called with a single argument, and would be expected
to return a character string containing the extra characters. The single
argument would specify how many characters should be returned (typically
this would be 1, unless internal analysis by the regex engine can deduce
that more than one character will be required). Returning fewer than the
requested number of characters would typically indicate a premature
end-of-string and would probably trigger backtracking and/or failure to
match.

When the match is finished, the subroutine would be called one final time,
and passed two arguments: a string containing the "unused" characters (what
would be $' for a fixed string), and a flag set to 1. The subroutine
could use this call to push-back (or cache) unused data. In the case of
a failure to match (or success of the !~ operator), every character requested
during the match would be sent back.

A typical structure for a subroutine against which a regex was matched
would therefore be:

        sub s {
            if ($_[1]) {                # "putback unused data" request
                recache($_[0]);
            }
            else {                      # "send more data" request
                return get_chars(max=>$_[0])
            }
        }


=head2 Examples

The most obvious example would be matching against an input stream:

        sub { $_[1] ? $fh->pushback($_[0]) : $fh->getn($_[0]) } =~ /pat/;

which could also be written:

        ^1 ? $fh->pushback(^0) : $fh->getn(^0)  =~ /pat/;

Of course, it would often be useful to have a subroutine that returns a 
closure on a particular filehandle:

        sub fhmatch { ^1 ? $_[0]->pushback(^0) : $_[0]->getn(^0) }

        fhmatch($fh)     =~ /pat/
        fhmatch(\*STDIN) =~ /pat/
        # etc.

In fact, this might be so commonly useful that matching against a
file handle should be made to work directly. That is:

        $fh =~ /pat/
        \*STDIN =~ /pat/
        
One could then do interactive lexing cleanly:

        until (eof $fh) {
                switch ($fh) {
                        /^\s*/;                 # skip leading whitespace
                        case /^(lexeme1)/       { push @tokens, $1=>LEX1 }
                        case /^(lexeme2)/       { interact_somehow }
                        case /^(lexeme3)/       { push @tokens, $1=>LEX3 }
                        # etc.
                }
        }

Note the use of the proposed PAIR data structure to store tokens
in the above example.

Because the character source is a subroutine, one could also match against 
data coming out of a socket:

        my $cache = "";

        sub matching_socks {
                if ($_[1]) { $cache .= $_[0]; return }  # putback
                if (length($cache) < $_[0]) {           # not enough cached
                        my $extra;                      # so get some more
                        recv(SOCKET, $extra, $_[0]-length($cache));
                        $cache .= $extra;
                }
                return substr($cache,0,$_[0],"");
        }

        switch (\&matching_socks) {
                case /pat1/ { action1() }
                case /pat2/ { action1() }
                case /pat3/ { action1() }
                #etc.
        }


or any other source:

        sub mega_ape {
                return join "", map {['a'..'z',(' ')x6]->[rand 32]} (1..$_[0])
                        unless $_[1]
        }

        \&mega_ape =~ /Now is the Winter of our discontent.../i;

        print "Art after ", length($`), "chars\n";


=head1 IMPLEMENTATION

Dammit, Jim, I'm a doctor, not an magician!

Probably needs to be integrated with IO disciplines too.


=head1 REFERENCES

RFC 22 : Builtin switch statement 

RFC 23 : Higher order functions 

RFC 84 : Replace => (stringifying comma) with => (pair constructor) 


Reply via email to