How to refer to previously matched items in a grammar?
Asked Answered
F

2

3

I am trying to parse a BibTeX author field, and split it into its separate authors. This will help me rewriting the initials of each author. Here is a minimal example:

use v6;

my $str = '{Rockhold, Mark L and Yarwood, RR and Selker, John S}';

grammar BibTexAuthor {
    token TOP {
        <all-text> 
    }
    token all-text {
        '{' <authors> '}' 
    }
    token authors { 
        [<author> [' and ' || <?before '}'>]]+
    }
    token author {
        [<-[\s}]> || [' ' <!before 'and '>]]+
    }
}

class BibTexAuthor-actions {
    method TOP($/) {
        say $/;
        print "First author = ";
        say $<author>.made[0];
        make $/.Str;
    }
    method all-text($/) {
        make $/.Str;
    }
    method authors($/) {
        make $/.Str;
    }
    method author($/) {
        make $/.Str;
    }
}
my $res = BibTexAuthor.parse( $str, actions => BibTexAuthor-actions.new).made;

Output:

「{Rockhold, Mark L and Yarwood, RR and Selker, John S}」
 all-text => 「{Rockhold, Mark L and Yarwood, RR and Selker, John S}」
  authors => 「Rockhold, Mark L and Yarwood, RR and Selker, John S」
   author => 「Rockhold, Mark L」
   author => 「Yarwood, RR」
   author => 「Selker, John S」
First author = Nil

Why am I not able to extract the first author in the TOP method?

Faroff answered 12/11, 2017 at 11:36 Comment(5)
method authors($/) { make @<author>.map(*.made) }Quintonquintuple
@BradGilbert Thanks for the suggestion, but I cannot make that work. If I replace my method authors with your suggestion, I still get Nil output for first author.Tartan
$<all-text><authors><author>[0];Pigfish
@Pigfish Thanks, that seems to work!Tartan
moritz++ answer below is better than mine. I don't mind if you re-assign the accepted answer. (if that's possible on stackoverflow)Pigfish
B
5

Why am I not able to extract the first author in the TOP method?

Because you are not really extracting any data in the action methods. All you do is attach the string of the match to $/.made, which is not actually the data you want in the end.

If you want to have separate authors in the end, you should make an array of authors in the authors action method. For example:

use v6;

my $str = '{Rockhold, Mark L and Yarwood, RR and Selker, John S}';

grammar BibTexAuthor {
    token TOP {
        <all-text> 
    }
    token all-text {
        '{' <authors> '}' 
    }
    token authors { 
        [<author> [' and ' || <?before '}'>]]+
    }
    token author {
        [<-[\s}]> || [' ' <!before 'and '>]]+
    }
}

class BibTexAuthor-actions {
    method TOP($/) {
        make { authors => $<all-text>.made };
    }
    method all-text($/) {
        make $/<authors>.made;
    }
    method authors($/) {
        make $/<author>».made;
    }
    method author($/) {
        make $/.Str;
    }
}
my $res = BibTexAuthor.parse( $str, actions => BibTexAuthor-actions.new).made;

say $res.perl;

prints

${:authors($["Rockhold, Mark L", "Yarwood, RR", "Selker, John S"])}

so now the .made of the top-level match is a hash, where the authors key holds an array. If you want to access the first author, you can now say

say $res<authors>[0];

to get Rockhold, Mark L

Bridging answered 12/11, 2017 at 14:2 Comment(0)
P
4
$<all-text><authors><author>[0];

Note, that I have no idea how grammars work until now. I'm learning the language as you do.

But just by looking at the datastrucure it's easy to realize it's a tree and where in that tree the value you are looking for is.

You can output any datastructure by saying

dd $someStructure;
say $someStructure.perl;

and if you find that unreadable, you can try one of the Dumper Modules

Pigfish answered 12/11, 2017 at 13:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.