3 votes

Comportement étrange lors de l'analyse syntaxique d'un langage impératif avec Parsec

J'essaie d'analyser un fragment du langage Abap avec Parsec en haskell. Les déclarations en Abap sont délimitées par des points. La syntaxe pour la définition des fonctions est :

FORM <name> <arguments>.
    <statements>.
ENDFORM.

Je vais l'utiliser comme un exemple minimal. Voici ma tentative d'écrire le type correspondant en haskell et le parseur. Le site GenStatement -Constructeur est pour toutes les autres déclarations sauf la définition de la fonction comme décrit ci-dessus.

module Main where

import Control.Applicative
import Data.Functor.Identity

import qualified Text.Parsec as P
import qualified Text.Parsec.String as S
import Text.Parsec.Language
import qualified Text.Parsec.Token as T

type Args = String
type Name = String

data AbapExpr -- ABAP Program
   = Form Name Args [AbapExpr]
   | GenStatement String [AbapExpr]
   deriving (Show, Read)

lexer :: T.TokenParser ()
lexer = T.makeTokenParser style
  where
    caseSensitive = False
    keys = ["form", "endform"]
    style = emptyDef
        { T.reservedNames = keys
        , T.identStart = P.alphaNum <|> P.char '_'
        , T.identLetter = P.alphaNum <|> P.char '_'
        }

dot :: S.Parser String
dot = T.dot lexer

reserved :: String -> S.Parser ()
reserved = T.reserved lexer

identifier :: S.Parser String
identifier = T.identifier lexer

argsP :: S.Parser String
argsP = P.manyTill P.anyChar (P.try (P.lookAhead dot))

genericStatementP :: S.Parser String
genericStatementP = P.manyTill P.anyChar (P.try dot)

abapExprP = P.try (P.between (reserved "form")
                             (reserved "endform" >> dot)
                             abapFormP)
    <|> abapStmtP
  where
    abapFormP = Form <$> identifier <*> argsP <* dot <*> many abapExprP
    abapStmtP = GenStatement <$> genericStatementP <*> many abapExprP

En testant l'analyseur avec l'entrée suivante, on obtient un comportement étrange.

-- a wrapper for convenience
parse :: S.Parser a -> String -> Either P.ParseError a
parse = flip P.parse "Test"

testParse1 = parse abapExprP "form foo arg1 arg2 arg2. form bar arg1. endform. endform."

résulte en

Right (GenStatement "form foo arg1 arg2 arg2" [GenStatement "form bar arg1" [GenStatement "endform" [GenStatement "endform" []]]])

il semble donc que la première branche échoue toujours et que seule la deuxième branche générique réussisse. Cependant, si la deuxième branche (analyse des instructions génériques) est commentée, l'analyse des formulaires réussit soudainement :

abapExprP = P.try (P.between (reserved "form")
                             (reserved "endform" >> dot)
                             abapFormP)
    --    <|> abapStmtP
  where
    abapFormP = Form <$> identifier <*> argsP <* dot <*> many abapExprP
    -- abapStmtP = GenStatement <$> genericStatementP <*> many abapExprP

Maintenant, nous obtenons

 Right (Form "foo" "arg1 arg2 arg2" [Form "bar" "arg1" []])

Comment cela est-il possible ? Il semble que la première branche réussisse, alors pourquoi cela ne fonctionne-t-il pas dans le premier exemple - qu'est-ce que je rate ?

Merci d'avance !

2voto

mschmidt Points 2204

Il me semble que votre analyseur syntaxique genericStatementP analyse syntaxique tout jusqu'à ce qu'un point apparaisse (vous utilisez P.anyChar ). Par conséquent, il ne reconnaît pas les mots-clés réservés pour votre lexer.

Je pense que vous devez définir :

type Args = [String]

et :

argsP :: S.Parser [String]
argsP = P.manyTill identifier (P.try (P.lookAhead dot))

genericStatementP :: S.Parser String
genericStatementP = identifier

Avec ces modifications, j'obtiens le résultat suivant :

Right (Form "foo" ["arg1","arg2","arg2"] [Form "bar" ["arg1"] []])

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X