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 !