For Gentoo package dependencies I need to parse the following:
"A package name may contain any of the characters [A-Za-z0-9+_-]. It
must not begin with a hyphen, and must not end in a hyphen followed by
one or more digits."

The package name is followed by the version specification so basically
unless we first find the whole atom and then start parsing from end to
start we need to parse package name until version starts. Here's my two
approaches but neither is able to pass my tests perfectly:

pn      :       pn_start
        |       pn_start pn_middle* pn_part 
{!pn_end.matcher($pn.text).matches()}?;

// https://wincent.com/wiki/PEG-style_predicates_in_ANTLR
pn_middle:
        (pn_part ((pn_follows)=>{false}?| ) )=> pn_part;
pn_start : name_part|'+'|'_';
pn_part:  name_part|'+'|'_'|'-';

pn_follows
:
         {$versioned_dep.size() > 0}?=> version_spec (WS|EOF)
         | {$versioned_dep.size() == 0}?=> (WS|EOF);

Here antlr doesn't seem to generate anything for the predicate thing:

    public final void pn_middle() throws RecognitionException {
        try {
            // Depend.g:80:10: ( ( pn_part ( ( pn_follows )=>{...}? | )
)=> pn_part )
            // Depend.g:81:2: ( pn_part ( ( pn_follows )=>{...}? | ) )=>
pn_part
            {
            pushFollow(FOLLOW_pn_part_in_pn_middle364);
            pn_part();

            state._fsp--;
            if (state.failed) return ;

            }

        }
        catch (RecognitionException re) {
            reportError(re);
            recover(input,re);
        }
        finally {
        }
        return ;
    }

My other approach is trying to make use of options greedy:

pn      :       pn_start pn_end? {!pn_end.matcher($pn.text).matches()}?;

pn_end  :       (options { greedy=false;} : pn_part)* (pn_part pn_follows)=>
pn_part;

But this does not pass the testsuite either. The version with greedy and
the testsuite is attached. What approach do you recommend for parsing this?

Regards,
Petteri
grammar Depend;

options {
        backtrack=true;
}

@header {
        import java.util.regex.Pattern;
}

@members {
public class EAPIFeatures
{
        public static final boolean DEFAULT = true;
        public boolean STRONG_BLOCK = DEFAULT;
        public boolean SLOT_DEPENDS = DEFAULT;
        public boolean USE_DEPENDS = DEFAULT;
}

        private EAPIFeatures features = new EAPIFeatures();
        private Pattern pn_end = Pattern.compile(".*-\\d+");
        private Pattern version_char = Pattern.compile("[a-z]");
        public DependParser(TokenStream input, EAPIFeatures features) {
                this(input);
                this.features = features;
        }
}


depend: WS? expr WS?;

expr    :       expr_type (WS expr_type)*;

expr_type:
        any_of | use_conditional | pkg_dep;

any_of
        :       '||' WS '(' WS (expr WS)* ')';

use_conditional
        :       use_flag '?' WS '(' WS (expr WS)* ')';

pkg_dep
        :       block_oper? (versioned_dep | qpn);

versioned_dep
scope {
        boolean needs_version;
}:      operator qpn version_spec;

block_oper      :       '!' ({features.STRONG_BLOCK}?=> '!'?);

version_spec
        :       '-' eapi0_version_spec ( {features.SLOT_DEPENDS}?=> slot_dep? ) 
( {features.USE_DEPENDS}?=> use_dep? );

use_dep :       '[' use_dep_atom (',' use_dep_atom)* ']';

use_dep_atom
        :       ('!'|'-')? use_flag (EQUALS|'?')?;

slot_dep:
        ':' slot_name;


eapi0_version_spec
        : INTEGER ('.' INTEGER)* (ALPHA 
{version_char.matcher($ALPHA.text).matches()}?)? state_suffix* revision?;

state_suffix
        : '_' STATE INTEGER?;

revision: REVISION_START INTEGER;

qpn     :        category '/' pn;

//pn    :       pn_start ((options { greedy=false; } : pn_char)* (pn_char 
pn_follows)=>pn_char)? {!pn_end.matcher($pn.text).matches()}?;
pn      :       pn_start pn_end? {!pn_end.matcher($pn.text).matches()}?;

pn_end  :       (options { greedy=false;} : pn_part)* (pn_part pn_follows)=> 
pn_part;

// https://wincent.com/wiki/PEG-style_predicates_in_ANTLR
pn_middle:
        (pn_part (pn_follows| ) )=> pn_part;
pn_start : name_part|'+'|'_';
pn_part:  name_part|'+'|'_'|'-';

//pn_follows: version_spec? WS|EOF;

pn_follows
:
         {$versioned_dep.size() > 0}?=> version_spec (WS|EOF) 
         | {$versioned_dep.size() == 0}?=> (WS|EOF);

//(alphanum|'+'|'_') (alphanum|'+'|'_'|DOT|'-')*
slot_name
        :       name[true,true,true,false];

// must begin with alphanumerics
use_flag:       name[false,false,false,true];

category:       name[true,true,true,false];

name[boolean start_plus,
     boolean start_under,
     boolean end_dot,
     boolean end_at]:
        (
        name_part
        | {start_plus}? '+'
        | {start_under}? '_'
        )

        (
        name_part
        | '+'
        | '_'
        | '-'
        | {end_dot}? '.'
        | {end_at}? '@'
        )*;

name_part:      INTEGER|ALPHA|STATE|REVISION_START;

REVISION_START: '-r';

INTEGER :       DIGIT+;

STATE: 'alpha'|'beta'|'pre'|'rc'|'p';

ALPHA: (LOWER|UPPER)+;

EQUALS: '=';

operator:       '<' | '<=' | EQUALS | '~' | '>=' | '>';

WS  : (' '|'\r'|'\t'|'\u000C'|'\n')+; //There are places of mandatory ws so 
don't hide it

fragment DIGIT  :       '0'..'9';
fragment LOWER  :       'a'..'z';
fragment UPPER  :       'A'..'Z';
gunit Depend;
depend:
"  ||  ( app-foo/bar lol? ( >=dev-java/lol-3 )  )  " OK

any_of:
"|| ( app-foo/bar app-foo/another )" OK
"||( app-foo/bar )" FAIL
"|| (app-foo/bar )" FAIL
"|| ( app-foo/bar)" FAIL

use_conditional:
"foo? ( app-foo/bar )" OK
"foo ? ( )" FAIL
"foo?( app-foo/bar )" FAIL


pkg_dep:
">=app-cat/pkg-3" OK
"<app-cat/pkg-1.1.1a_alpha-r4" OK
"cat.a.gory/packageName-a" OK
"cat.a.gory/packageName-" OK
"=.cat.a.gory/packageName-12a" FAIL
"cat/foo-3" FAIL
">=app-cat/pkg" FAIL

versioned_dep:
"=cat.a.gory/packageName-1" OK
"=cat.a.gory/packageName+a-12a" OK
"=cat.a.gory/packageName-a-12a" OK
"=cat.a.gory/packageName-a-12a" OK
"=cat.a.gory/packageName--12a" OK


eapi0_version_spec:
"1.1.1" OK
"1.22.333a" OK
"1.1.1a-r3" OK
"3" OK
"2_alpha_beta" OK
"12a" OK

state_suffix:
"_alpha22" OK
"_alpha2a" FAIL
"_foo" FAIL

revision:
"-r123" OK
"-ra" FAIL
"-r123a" FAIL

qpn:
"app-foo/bar" OK

slot_name:
".SLOT" FAIL
"alpha" OK
"beta" OK
"123" OK
"foo" OK
"a-r" OK

use_flag:
"foo" OK
"foo+" OK
"foo_a" OK
"f@@@" OK
"f...@2" OK
"f-r1" OK
"alpha" OK
"123" OK
"@@" FAIL
"+FOO" FAIL

category:
"foo" OK
"foo1" OK
"11foo" OK
"app-foo" OK
"virtual" OK
"alpha" OK
"beta" OK
"foo--" OK
"123" OK
"a-r" OK
"cat.a.gory" OK
"-foo" FAIL
".foo" FAIL
".foo." FAIL

pn:
"_foo" OK
"_foo-bar" OK
"+bar11+" OK
"foo--" OK
"app-foo-bar" OK
"pkg-a" OK
"-laa" FAIL
"foo-12" FAIL
"foo-1" FAIL
"alpha" OK
"beta" OK
"123" OK
"core" OK
"core-1.2-r3" FAIL

pn_follows:
"       " OK
"1.1" FAIL

operator:
"<" OK
"<=" OK
"=" OK
"~" OK
">=" OK
">" OK

INTEGER:
"" FAIL
"1213" OK
"1221a" FAIL

UPPER:
"A" OK
"p" FAIL
"CC" FAIL

LOWER:
"P" FAIL
"k" OK
"pp" FAIL

WS:
" " OK
"  " OK
"       " OK
"ff" FAIL

Attachment: signature.asc
Description: OpenPGP digital signature

List: http://www.antlr.org/mailman/listinfo/antlr-interest
Unsubscribe: 
http://www.antlr.org/mailman/options/antlr-interest/your-email-address

Reply via email to