String Scanner: A Ruby Inspired Parser

By: w1ldc4rd-w1z4rd
[ BACK... ]

In the world of Perl programming, we often encounter tasks that require parsing and manipulating strings. Today, we’re going to explore my implementation of a string scanner in Perl, inspired by Ruby’s StringScanner. This powerful tool allows us to traverse a string, find patterns, and keep track of our position - all with a clean, functional interface.

Let’s dive into the code and break down its functionality:

#!/usr/bin/env perl

use strict;
use warnings;

package strscan;

sub create
{
    my $string = qq|@_|;
    @_ = undef;  # Clear @_ to free memory

    # start position
    my $pos = 0;

    # end of string position
    my $eos = length ( $string );

    return
    {
        pos => sub { return $pos }, # Closure to return current position
        mod_pos => sub { $pos = shift }, # Closure to modify position
        eos_check => sub { return $eos == $pos ? 0 : 1; }, # Check if not at end of string
        find => sub # Closure to find regex
        {
            my $regex = shift;

            # Match regex against substring from current position
            if ( my ($found) = substr ( $string, $pos ) =~ m~($regex)~ )
            {
                # $-[0] contains the start offset of the match within the substring
                my ( $start, $length ) = ( [0], length ( $found ) );

                # Update position: add start offset and length of match
                $pos +=  $start + $length;

                return # Return hash ref with match details
                {
                    pos => $pos,
                    match_start => $start,
                    match_length => $length,
                    match =>  $found,
                }
            }
            else
            {
                $pos = $eos;  # If no match, set position to end of string
                return undef;
            }
        }
    }
}

1;

__END__

This code defines a package called strscan with a single function create. Let’s break down what’s happening:

The find function is where the real magic of our string scanner happens:

Example Use

Now, let’s look at how we can use this scanner:

# Demo

package main;

use Data::Dumper;
use feature qw|say|;

my $scan = strscan::create ( 'This is just a test!' );

say q|start position: |, $scan->{pos}(), qq|\n|; # return position

while ( $scan->{eos_check}() ) 
{ 
    if ( my $match = $scan->{find}('\w+') )
    { 
        say q|match: |, $match->{match};
        say q|pos: |, $match->{pos};
        say q||;
    }
}

say q|end position: |, $scan->{pos}(); # return position

In This Demo:

This scanner provides a flexible way to parse strings, allowing us to easily move through the string and find patterns. It’s particularly useful for tasks like tokenizing input or parsing structured text.

The use of closures in this implementation is a powerful Perl technique. It allows us to maintain state (the position and string) without using global variables, providing a clean and encapsulated interface.

In conclusion, this Perl string scanner demonstrates how we can create powerful, Ruby-inspired tools using Perl’s flexible syntax and functional programming capabilities. It’s a testament to Perl’s expressiveness and ability to handle complex string manipulation tasks with elegance.

Copyright ©️ 2024 perl.gg