Quick actions

cmd+k|ctrl+k

Navigation

Languages

Example Glob Grammars

Snippet info

Language

Raku

Visibility

public

Author

zostay

Created

2016-12-21T22:57:46Z

Updated

2016-12-21T23:00:24Z

use v6;

grammar Glob {
    token TOP { <term>+ }
    token term {
        || <match>
        || <char>
    }
    token match { <match-any> | <match-one> }
    token match-any { '*' }
    token match-one { '?' }
    token char { . }
}

class GlobMatcher {
    has @.terms;

    method ACCEPTS(Str:D $s) {
        my @backtracks = [ \([ $s.comb ], [ @!terms ]) ];
        BACKTRACK: while @backtracks.pop -> (@letters, @terms) {
            LETTER: while @letters.shift -> $c {
                last LETTER unless @terms;

                my $this-term = @terms.shift;
                my $next-term = @terms[0];

                given $this-term {
                    when '*' {
                        # Continue to the next term if we can
                        if @terms and $next-term ne '*' | '?' and $c eq $next-term {
                            push @backtracks, ([ @letters ], [ '*', |@terms ]);
                            redo;
                        }

                        # Match anything, and try again next round
                        unshift @terms, $this-term;
                    }

                    # We have a letter, so we match!
                    when '?' { }

                    # Only match exactly
                    default {
                        # If not an exact match, we fail; try again if we can
                        next BACKTRACK if $c ne $this-term;
                    }
                }
            }

            # If we matched everything, we succeed
            return True unless @terms;

            # Otherwise, try the next backtrack, if any
        }

        # We ran out of back tracks, so we fail
        False;
    }
}

class GlobAction {
    method TOP($/)       { make GlobMatcher.new(terms => $<term>».made) }
    method term($/)      { make $/.values».made.first }
    method match($/)     { make $/.values».made.first }
    method match-any($/) { make '*' }
    method match-one($/) { make '?' }
    method char($/)      { make ~$/ }
}

my $matcher = Glob.parse("*.txt", :actions(GlobAction.new)).made;
for <foo.txt bar.txt baz.html blah.blah.txt> -> $f {
    say "$f matches" if $f ~~ $matcher;
}

sub glob($glob, :$globlang = Glob) {
    $globlang.parse($glob, :actions(GlobAction.new)).made;
}

for <foo.txt bar.txt baz.html blah.blah.txt>.grep(glob('*.txt')) -> $f {
    next unless $f ~~ glob('*.txt');
    say "$f matches";
}

<foo.txt bar.txt baz.html blah.blah.txt>.grep(glob('*.txt')).say;

grammar SQLGlob is Glob {
    token match-any { '%' }
    token match-one { '_' }
}

<foo.txt bar.txt baz.html blah.blah.txt>.grep(glob('%.txt', :globlang(SQLGlob))).say;
INFO