2010-12-02 37 views
2

Je suis en train de développer un BNF pour la notation algébrique des échecs et je suis tombé sur un cas intéressant, l'entrée allant à la mauvaise non terminale.BNF: l'entrée va à un mauvais non-terminal

Mon départ règle BNF est la suivante (notez que cela ne comprend pas intentionnellement roque ou des notes):

algebraic_notation : piece start_position capture end_position promotion 

piece, start_position, capture et promotion peut être vide, permettant ainsi un mouvement comme 'd4'. Le problème est que lorsqu'un tel déplacement est entré, l'entrée ('d4') est prise par start_position, ce qui entraîne une erreur b/c il n'y a plus d'entrée pour end_position, qui ne peut pas être vide.

La solution de contournement/contournement évidente est de permettre à end_position d'être vide, puis de vérifier si nous avons des entrées et d'agir en conséquence.

Cela fonctionne, mais j'aimerais savoir s'il existe un moyen de résoudre ce problème. Est-il possible que l'entrée ne passe pas au premier symbole correspondant si l'expression entière ne correspond pas?

Une autre question est de savoir si c'est un comportement standard pour BNF, ou un problème avec le yaccer que j'utilise: PLY v 3.3. Essayé en utilisant flex/bison et obtenu la même chose.

Il semble donc que ce n'est pas spécifique à PLY.

Voici toutes les règles pertinentes pour l'exhaustivité:

algebraic_notation : piece start_position capture end_position promotion 

piece : KING 
     | QUEEN 
     | BISHOP 
     | KNIGHT 
     | ROOK 
     | pawn 

pawn : empty 

start_position : FILE 
       | NUMBER 
       | FILE NUMBER 
       | empty 

end_position : FILE NUMBER 
       | empty     // this line is the hack/workaround 

capture : CAPTURE 
     | empty 

promotion : EQUAL QUEEN 
      | EQUAL ROOK 
      | EQUAL KNIGHT 
      | EQUAL BISHOP 
      | empty 

empty : 
+0

Yacc peut-être trop pour ce problème? – Cam

+0

@cam Peut-être. Mais l'analyse des chaînes à la main n'est pas tout à fait claire ou lisible dans mon expérience. – Matthew

+0

Aussi, même si BNF est trop puissant pour cette application particulière, il est toujours possible de rencontrer ce problème dans une grammaire plus complexe. De toute façon, j'ai une solution de contournement/hack; Je simplement que je veux utiliser une meilleure solution si possible. – Matthew

Répondre

0

Je ne l'ai pas utilisé PLI, et sans voir les dossiers complets flex/bison vous essayées je pourrais en prendre à un non-problème, mais Il me semble que vous ne donnez pas à l'analyseur l'idée que plus rien ne vient pour la règle actuelle algebraic_notation. Vous ne dites pas comment vous savez que l'entrée 'd4' correspond à start_position, mais si l'analyseur savait qu'il avait tous les jetons pour la règle, et que le seul jeton non vide est end_position, il devrait correspondre à 'd4' cette. Qu'en est-il de l'introduction d'un jeton qui marque la fin d'une ligne, comme EOL. Donc, votre première règle devient:

algebraic_notation : piece start_position capture end_position promotion EOL 

et l'analyseur voit maintenant le jeton « d4 » suivi par EOL - ce que ça change le comportement?

+0

Non. Ce qui se passe, c'est que 'd4' va à 'start_position' et nous devons ensuite faire correspondre EOL à la fois' end_position' et 'EOL'. Cela ne change pas le fait que l'analyseur ne semble pas regarder la règle entière, mais correspond simplement à l'entrée de la première chose dans la règle, indépendamment du fait que cela ne fasse pas correspondre la règle entière. – Matthew

+1

normal ils ne regardent qu'un seul jeton devant –

0

Qu'est-ce qui se passe si vous enveloppez start_position capture end_position dans un bloc central, et de supprimer FILE NUMBER de start_pos, à quelque chose comme ceci:

middle: start_pos capture end_pos 
     | end_pos capture end_pos 
     | capture end_pos 

start_pos : FILE 
     | NUMBER 
     | empty 

end_pos : FILE NUMBER 

capture : CAPTURE 
     | empty 
+0

Même résultat. A moins qu'il y ait un moyen de spécifier que la première ligne de 'middle' a préséance sur la seconde, 'd4' ira juste au premier' end_pos' de la deuxième ligne, ce qui aura pour résultat de ne pas avoir d'entrée pour le 2nd 'end_pos '. – Matthew

0

Cette question est une bonne illustration d'un problème est la théorie de la science informatique, la suppression de productions epsilon (ou vides) d'une grammaire. Les problèmes d'ambiguïté de la notation d'échecs peuvent être résolus (pour yacc ou PLY) en simplifiant la grammaire pour supprimer les productions vides. Il y a beaucoup de documentation à ce sujet sur SO/SE et sur d'autres sites. J'ajoute une bibliographie pour le lecteur intéressé.

En effectuant une transformation sans esprit des règles pour éliminer les productions aveugles/vides/epsilon nous obtenons la CFG suivante:

algebraic_notation : piece start_position capture end_position promotion 
        | piece start_position capture end_position 
        | piece start_position capture promotion 
        | piece start_position end_position promotion 
        | piece capture end_position promotion 
        | piece start_position capture 
        | piece start_position end_position 
        | piece capture end_position 
        | piece start_position promotion 
        | piece capture promotion 
        | piece end_position promotion 
        | piece promotion 
        | piece end_position 
        | piece capture 
        | piece start_position 
        | piece 
        | start_position capture end_position promotion 
        | start_position capture end_position 
        | start_position capture promotion 
        | start_position end_position promotion 
        | capture end_position promotion 
        | start_position capture 
        | start_position end_position 
        | capture end_position 
        | end_position promotion 
        | start_position 
        | capture 
        | end_position 
        | promotion 
piece : KING 
     | QUEEN 
     | BISHOP 
     | KNIGHT 
     | ROOK 

start_position : FILE 
       | NUMBER 
       | FILE NUMBER 

end_position : FILE NUMBER 

capture : CAPTURE 

promotion : EQUAL QUEEN 
      | EQUAL ROOK 
      | EQUAL KNIGHT 
      | EQUAL BISHOP 

(Cela pourrait probablement être simplifié en supprimant les combinaisons qui ne pouvaient pas se produire dans la notation d'échecs , mais c'est un exercice pour le lecteur).

Bibliographie

Les meilleurs livres pour ce sont probablement:

ou tout simplement aller aux diapositives de la classe de Jeff Ullman:

Ou un tas de questions connexes sur le SO/SE:

+0

L'élimination de TOUTES les règles epsilon est généralement exagérée. Il est généralement beaucoup mieux de laisser votre générateur d'analyseur vous guider, et il suffit d'éliminer les règles epsilon qui causent des conflits ... –

1

Le problème est que vous ne tenez pas compte du changement/réduire les conflits que vous obtenez de votre générateur d'analyseur syntaxique. Alors que yacc/bison (et vraisemblablement PLY) va résoudre les erreurs pour vous, cette résolution pourrait ne pas faire ce que vous voulez, et pourrait aboutir à un analyseur qui analyse une langauge autre que celle que vous essayez d'analyser. Chaque fois que vous obtenez un décalage/réduit (ou réduit/réduit) conflit d'un générateur d'analyseur LR, vous devez vraiment comprendre quel est le conflit (et pourquoi il se produit) pour savoir si vous pouvez l'ignorer ou si vous avez besoin réparer. permet donc corriger votre grammaire en se débarrassant de la « bidouille » (ce qui est manifestement erronée et non quelque chose que vous voulez analyser), ainsi que la règle « vide » inutile (qui confond juste les choses):

%token FILE NUMBER 
%% 
algebraic_notation : piece start_position capture end_position promotion 
piece : 'K' | 'Q' | 'B' | 'N' | 'R' | /*pawn*/ 
start_position : FILE | NUMBER | FILE NUMBER | /*empty*/ 
end_position : FILE NUMBER 
capture : 'x' | /*empty*/ 
promotion : '=' 'Q' | '=' 'R' | '=' 'N' | '=' 'B' | /*empty*/ 

Maintenant, quand vous lancez ceci dans 'bison -v' (TOUJOURS utiliser -v pour obtenir le fichier de sortie verbeux - je ne suis pas sûr de l'équivalent de PLY), vous obtenez le message concernant un changement/réduction de conflit, et si vous regardez dans le fichier .output vous pouvez voir ce qu'il est:

state 7 

    1 algebraic_notation: piece . start_position capture end_position promotion 

    FILE shift, and go to state 9 
    NUMBER shift, and go to state 10 

    FILE  [reduce using rule 11 (start_position)] 
    $default reduce using rule 11 (start_position) 

    start_position go to state 11 

Ce que vous dit après avoir vu un piece, quand le prochain jeton est FILE, il ne sait pas s'il doit se décaler (en traitant le FILE comme faisant partie du start_position) ou réduire (en donnant un start_position vide). C'est parce qu'il faut plus d'attention pour voir s'il y a une seconde position à utiliser comme end_position pour savoir quoi faire, donc simplement ignorer le conflit résultera en un analyseur qui échoue à analyser beaucoup de choses valides (en gros, n'importe quoi start_position et capture). La meilleure façon de résoudre un conflit lié à la prévisualisation - réduire un conflit impliquant une production vide comme celle-ci (ou pratiquement tout conflit impliquant une production vide, en réalité) est de débloquer la grammaire - se débarrasser de la règle vide et dupliquer toute règle qui utilise le non-terminal à la fois avec et sans lui.Dans votre cas, cela vous donne les règles:

algebraic_notation : piece capture end_position promotion 
algebraic_notation : piece start_position capture end_position promotion 
start_position : FILE | NUMBER | FILE NUMBER 

(les autres règles ne changent pas) Avec que vous avez encore un conflit de décalage réduire:

state 7 

    1 algebraic_notation: piece . capture end_position promotion 
    2     | piece . start_position capture end_position promotion 

    FILE shift, and go to state 9 
    NUMBER shift, and go to state 10 
    'x'  shift, and go to state 11 

    FILE [reduce using rule 14 (capture)] 

    start_position go to state 12 
    capture   go to state 13 

En fait, nous venons Déplacé le conflit une étape et maintenant le problème avec la règle capture vide. Nous avons donc unfactor cela aussi:

algebraic_notation : piece end_position promotion 
algebraic_notation : piece capture end_position promotion 
algebraic_notation : piece start_position end_position promotion 
algebraic_notation : piece start_position capture end_position promotion 
capture : 'x' 

et maintenant des rapports de bison pas plus de conflits, afin que nous puissions être raisonnablement confiant, il analysera la façon dont nous voulons. Vous pouvez le simplifier un peu plus en supprimant la règle capture et en utilisant un littéral 'x' dans la règle algebraic_notation. Personnellement, je préfère cela, car je pense qu'il est plus clair d'éviter l'indirection inutile:

%token FILE NUMBER 
%% 
algebraic_notation : piece end_position promotion 
algebraic_notation : piece 'x' end_position promotion 
algebraic_notation : piece start_position end_position promotion 
algebraic_notation : piece start_position 'x' end_position promotion 
piece : 'K' | 'Q' | 'B' | 'N' | 'R' | /*pawn*/ 
start_position : FILE | NUMBER | FILE NUMBER 
end_position : FILE NUMBER 
promotion : '=' 'Q' | '=' 'R' | '=' 'N' | '=' 'B' | /*empty*/