2017-10-14 11 views
3

J'essaye d'analyser un fragment du langage Abap avec Parsec en haskell. Les instructions dans Abap sont délimitées par des points. La syntaxe pour la définition de la fonction est:Comportement étrange analysant un langage impératif en utilisant Parsec

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

Je vais l'utiliser comme un exemple minimal. Voici ma tentative d'écrire le type correspondant dans haskell et l'analyseur. Le GenStatement -Constructeur est pour toutes les autres instructions sauf la définition de fonction comme décrit ci-dessus. Tester l'analyseur avec l'entrée suivante entraîne un comportement étrange.

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 

-- 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ésultats dans

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

il semble donc la première Brach échoue toujours et seulement la deuxième branche générique est réussie. Toutefois, si la deuxième branche (analyse des déclarations génériques) est commenté les formes d'analyse syntaxique réussit tout à coup:

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 est-ce possible? Il semble que la première branche réussisse alors pourquoi ne fonctionne-t-elle pas dans le premier exemple - qu'est-ce qui me manque?

Un grand merci à l'avance!

Répondre

2

Il me semble que votre analyseur syntaxique genericStatementP analyse n'importe quel caractère jusqu'à l'apparition d'un point (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 changements que je reçois le résultat suivant:

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

fonctionne comme un charme! Merci beaucoup! – jules