2016-09-15 2 views
0

Avoir la grammaire simple suivante, je voudrais analyser simultanément des chaînes et des nombres:prédicats sémantique pour la chaîne simultanée et numéro analyse syntaxique

grammar Simple; 

aRule : 'fs' '(' value["textual"] ')' ; 
bRule : 'fi' '(' value["numeral"] ')' ; 
cRule : 'f' '(' (value["textual"] | value["numeral"]) ')' ;  

value[String k] 
    : {$k.equals("any") || $k.equals("textual")}? string 
    | {$k.equals("any") || $k.equals("numeral")}? numeric 
    ; 

string 
    : STRING_LITERAL 
    ; 

numeric 
    : ('+' | '-')? INTEGER_LITERAL 
    ; 

STRING_LITERAL 
    : '\'' (~('\'' | '\r' | '\n') | '\'' '\'' | NEWLINE)* '\'' 
    ; 

INTEGER_LITERAL 
    : '0' | [1-9] [0-9]* 
    ; 

SPACES 
    : [ \t\r\n]+ -> skip 
    ; 

fragment NEWLINE     : '\r'? '\n'; 

Maintenant, je voudrais analyser les expressions suivantes:

fs('asdf') // works 
fi(512) // works 
f('asdf') // works 
f(512)  // fails 

Si je passe textual et numeral dans cRule, puis f('asdf') échoue et f(512) œuvres.

Des idées?

Update1

grammar Simple; 

rules : aRule | bRule | cRule ; 
aRule : 'fs' '(' value["textual"] ')' ; 
bRule : 'fi' '(' value["numeral"] ')' ; 
cRule : 'f' '(' (tRule | nRule) ')' ; 
tRule : value["textual"] ; 
nRule : value["numeral"] ; 
value[String k] 
    : {$k.equals("any") || $k.equals("textual")}? string 
    | {$k.equals("any") || $k.equals("numeral")}? numeric 
    ; 
string : STRING_LITERAL ; 
numeric : ('+' | '-')? INTEGER_LITERAL ; 

STRING_LITERAL : '\'' (~('\'' | '\r' | '\n') | '\'' '\'' | NEWLINE)* '\'' ; 
INTEGER_LITERAL : '0' | [1-9] [0-9]* ; 
SPACES   : [ \t\r\n]+ -> skip ; 

fragment NEWLINE : '\r'? '\n'; 

Même w/cette grammaire mise à jour --- comme suggérer par @GRosenberg --- pour f(512) je reçois toujours no viable alternative at input '512'. Encore une fois fs('asdf'), fi(512) et f('asdf') travail.

+1

* "cela ne fonctionne pas" * => c'est sans signification, dites-nous * quoi * et * comment * cela ne fonctionne pas, s'il vous plaît voir [mcve]. Aussi, * pourquoi * convoluez-vous votre grammaire comme ça? Pourquoi ne pas faire simplement: 'soit: string | numeric; 'et' aRule: A_STR_FUNC '(' string ')'; 'et ainsi de suite? –

+0

Ceci est juste un extrait simple d'une grammaire beaucoup plus compliquée. Dans la grammaire complète, je dois restreindre les expressions à certains endroits pour être seulement de type "string", int, ou date time, etc .. Donc, pour m'assurer qu'à ces endroits pas/any/expression est permise, je voulais suivre cette approche. –

+0

On dirait que [les prédicats dépendants du contexte ne jouent pas trop bien avec la prédiction] (https://github.com/antlr/antlr4/blob/master/doc/predicates.md#using-context-dependent-predicates).Je * pense * encore que ce que vous essayez de faire est réalisable sans prédicats cependant. –

Répondre

0

Le code généré pertinent est répertorié ci-dessous. Dans celui-ci, notez qu'un échec de contrainte de prédicat entraîne une exception. Ceci explique pourquoi le second alt est toujours ignoré - soit le premier alt est correct ou la règle entière échoue.

Si le code généré est en quelque sorte «incorrect» est une question probablement mieux adressée au groupe Google ANTLR4 ou à la page des problèmes Github.

Dans tous les cas, la solution consiste à séparer en utilisant les paragraphes:

cRule : F LPAREN (dRule | eRule) RPAREN EOF ; 
dRule : value["textual"] ; 
eRule : value["numeral"] ; 

UPDATE

La règle de valeur est celle qui doit être divisé en sous-règles:

cRule : F LPAREN (valuet["textual"] | valuen["numeral"]) RPAREN EOF ; 

valuet[String k] 
    : {$k.equals("any") || $k.equals("textual")}? string 
    ; 

valuen[String k] 
    : {$k.equals("any") || $k.equals("numeral")}? numeric 
    ; 

Testé /travaux.


public final ValueContext value(String k) throws RecognitionException { 
    ValueContext _localctx = new ValueContext(_ctx, getState(), k); 
    enterRule(_localctx, 2, RULE_value); 
    try { 
     setState(21); 
     _errHandler.sync(this); 
     switch (getInterpreter().adaptivePredict(_input, 1, _ctx)) { 
      case 1: 
       enterOuterAlt(_localctx, 1); { 
       setState(17); 
       if (!(_localctx.k.equals("any") || _localctx.k.equals("textual"))) 
        throw new FailedPredicateException(this, "$k.equals(\"any\") || $k.equals(\"textual\")"); 
       setState(18); 
       string(); 
      } 
       break; 
      case 2: 
       enterOuterAlt(_localctx, 2); { 
       setState(19); 
       if (!(_localctx.k.equals("any") || _localctx.k.equals("numeral"))) 
        throw new FailedPredicateException(this, "$k.equals(\"any\") || $k.equals(\"numeral\")"); 
       setState(20); 
       numeric(); 
      } 
       break; 
     } 
    } catch (RecognitionException re) { 
     _localctx.exception = re; 
     _errHandler.reportError(this, re); 
     _errHandler.recover(this, re); 
    } finally { 
     exitRule(); 
    } 
    return _localctx; 
} 
+0

Lorsque j'ai essayé de le reprocher (en C#), l'erreur que je recevais ne venait pas de là, mais de 'adaptivePredict', et c'était une exception' NoViableAltException', donc ce n'est * pas * un échec de prédicat. –

+0

J'ai essayé votre suggestion, mais malheureusement, cela ne fonctionne pas. Voir la grammaire mise à jour. –

+0

Intéressant. OP n'a pas spécifié, donc probablement en utilisant le générateur Java par défaut. Il produit des contraintes de prédicat * sur les alts, comme indiqué, qui sont distinctes de et en plus de l'évaluation réelle des prédicats. Peut être l'implémentation C# construit cela dans son adaptativePredict. – GRosenberg

1

Je suis d'accord avec Lucas, c'est une grammaire totalement overcomplicated. Si vous voulez n'accepter qu'une chaîne, alors, par tous les moyens, spécifiez seulement une chaîne dans votre grammaire. Pourquoi utiliser une règle de valeur avec toutes les options différentes et la limiter à une seule? C'est un coup typique dans le pied. Au lieu de cela faire comme ceci:

rules : aRule | bRule | cRule ; 
aRule : 'fs' '(' string ')' ; 
bRule : 'fi' '(' numberic ')' ; 
cRule : 'f' '(' (tRule | nRule) ')' ; 
tRule : string ; 
nRule : numeric ; 

Il est aussi beaucoup plus facile à lire si vous épelez ce que vous voulez que votre langue à regarder dehors, au lieu d'essayer de paramétrer une règle générique.

+0

Merci Mike! Oui, je suivrai le conseil de Lucas. –