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
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
