2009-07-23 6 views
8

G'day!Comment puis-je construire une grammaire Python propre dans ANTLR?

Comment puis-je construire une simple syntaxe ANTLR gérant des expressions multilignes sans avoir besoin de points-virgules ou de barres obliques inverses?

Je suis en train d'écrire un simple DSLs pour les expressions:

# sh style comments 
ThisValue = 1 
ThatValue = ThisValue * 2 
ThisOtherValue = (1 + 2 + ThisValue * ThatValue) 
YetAnotherValue = MAX(ThisOtherValue, ThatValue) 

Dans l'ensemble, je veux que mon application pour fournir le script avec quelques valeurs initiales nommées et tirer le résultat final. Je me suis accroché à la syntaxe, cependant. Je voudrais prendre en charge plusieurs expressions de ligne comme ce qui suit:

# Note: no backslashes required to continue expression, as we're in brackets 
# Note: no semicolon required at end of expression, either 
ThisValueWithAReallyLongName = (ThisOtherValueWithASimilarlyLongName 
           +AnotherValueWithAGratuitouslyLongName) 

J'ai commencé avec une grammaire ANTLR comme ceci:

exprlist 
    : (assignment_statement | empty_line)* EOF! 
    ; 
assignment_statement 
    : assignment NL!? 
    ; 
empty_line 
    : NL; 
assignment 
    : ID '=' expr 
    ; 

// ... and so on 

Il semble simple, mais je suis déjà en difficulté avec le : les nouvelles lignes

warning(200): StackOverflowQuestion.g:11:20: Decision can match input such as "NL" using multiple alternatives: 1, 2 
As a result, alternative(s) 2 were disabled for that input 

Graphiquement, en org.antlr.works.IDE:

Decision Can Match NL Using Multiple Alternatives http://img.skitch.com/20090723-ghpss46833si9f9ebk48x28b82.png

J'ai botté la grammaire autour, mais finissent toujours avec des violations de comportement attendu:

  • Une nouvelle ligne est nécessaire à la fin du fichier
  • Les lignes vides sont acceptables
  • Tout ce qui se trouve dans une ligne à partir de la dièse est annulé en tant que commentaire
  • Les affectations se terminent par la fin de la ligne, et non par des points-virgules.
  • Les expressions peuvent s'étendre sur plusieurs lignes si elles sont entourées entre parenthèses ets

Je peux trouver des exemples de grammaires ANTLR avec plusieurs de ces caractéristiques. Je trouve que lorsque je les coupe pour limiter leur expressivité à ce dont j'ai besoin, je finis par casser quelque chose. D'autres sont trop simples, et je les casse à mesure que j'ajoute de l'expressivité.

Quel angle dois-je prendre avec cette grammaire? Pouvez-vous pointer vers des exemples qui ne sont pas des langages triviaux ou complets?

Répondre

6

Je laisserais votre tokenizer faire le levage de charges lourdes plutôt que de mélanger vos règles de grammaire dans votre nouvelle ligne:

  • parenthèses Count, les crochets et les accolades, et ne génèrent pas de jetons NL alors qu'il ya des groupes non fermés . Cela vous donnera des continuations de ligne gratuitement sans que votre grammaire soit plus sage. Toujours générer un jeton NL à la fin du fichier, que la dernière ligne se termine ou non par un caractère '\n', vous n'avez donc pas à vous soucier d'un cas particulier d'une instruction sans NL. Déclarations toujours fin avec une NL.

Le deuxième point vous permettrait de simplifier la grammaire à quelque chose comme ceci:

exprlist 
    : (assignment_statement | empty_line)* EOF! 
    ; 
assignment_statement 
    : assignment NL 
    ; 
empty_line 
    : NL 
    ; 
assignment 
    : ID '=' expr 
    ; 
+0

Maintenant, je dois comprendre comment obtenir le tokenizer pour faire ce lourd levage. Retour à la documentation, je suppose. :) –

+0

John, ça me échappe toujours. Quelle est la syntaxe grammaticale ANTLR pour que le tokenizer insère NL avant EOF? –

+0

+1 Pour toujours terminer sur une nouvelle ligne, rend les choses tellement plus propres. Merci. – Craz

0

Comment cela?

exprlist 
    : (expr)? (NL+ expr)* NL!? EOF! 
    ; 
expr 
    : assignment | ... 
    ; 
assignment 
    : ID '=' expr 
    ; 
0

Je suppose que vous avez choisi de faire NL en option, parce que la dernière déclaration dans votre code d'entrée ne doit pas se terminer par un saut de ligne.

Bien que cela ait beaucoup de sens, vous rendez la vie beaucoup plus difficile pour votre analyseur. Les jetons séparateurs (comme NL) devraient être chéris, car ils désambiguïsent et réduisent le risque de conflits.

Dans votre cas, l'analyseur ne sait pas s'il doit analyser "affectation NL" ou "affectation ligne_vide". Il y a plusieurs façons de le résoudre, mais la plupart d'entre elles ne sont que des aides de bande pour un choix de conception imprudent.

Ma recommandation est un hack innocent: Rendre NL obligatoire, et toujours ajouter NL à la fin de votre flux d'entrée!

Cela peut sembler un peu désagréable, mais en réalité, il vous fera économiser beaucoup de maux de tête futurs.