Question

Je suis nouvelle à la fois Haskell et parsecs. Dans un effort pour en apprendre davantage sur la langue et cette bibliothèque en particulier, je suis en train de créer un analyseur qui peut analyser Lua les fichiers enregistrés variables. Dans ces variables fichiers peuvent prendre les formes suivantes:

varname = valeur

varname = {valeur, valeur, ...}

varname = {{valeur, valeur}, {valeur, valeur, ...}}

Je l'ai créé pour parseurs chacun de ces types, mais quand je les enchaîner avec le choix <|>. Opérateur je reçois une erreur de type

Couldn't match expected type `[Char]' against inferred type `Char'
  Expected type: GenParser Char st [[[Char]]]
  Inferred type: GenParser Char st [[Char]]
In the first argument of `try', namely `lList'
In the first argument of `(<|>)', namely `try lList'

Mon hypothèse est (bien que je ne le trouve pas dans la documentation) que chaque analyseur transmis à l'opérateur de choix doit retourner le même type. Voici le code en question:

data Variable = LuaString ([Char], [Char])
          | LuaList ([Char], [[Char]])
          | NestedLuaList ([Char], [[[Char]]])
          deriving (Show)

main:: IO()
main = do
       case (parse varName "" "variable = {{1234,\"Josh\"},{123,222}}") of
            Left err -> print err
            Right xs -> print xs 

varName :: GenParser Char st Variable
varName = do{
        vName <- (many letter);
        eq <- string " = ";
        vCon <- try nestList
             <|> try lList 
             <|> varContent;
        return (vName, vCon)}

varContent :: GenParser Char st [Char]
varContent =  quotedString 
    <|> many1 letter
    <|> many1 digit

quotedString :: GenParser Char st [Char]
quotedString = do{
         s1 <- string "\""; 
         s2 <- varContent;
         s3 <- string "\"";
         return (s1++s2++s3)}

lList :: GenParser Char st [[Char]]
lList = between (string "{") (string "}") (sepBy varContent (string ","))

nestList :: GenParser Char st [[[Char]]]
nestList = between (string "{") (string "}") (sepBy lList (string ","))
Était-ce utile?

La solution

C'est exact.

(<|>) :: (Alternative f) => f a -> f a -> f a

Remarquez comment les deux arguments sont exactement du même type.

Je ne comprends pas exactement votre type de données Variable. C'est ainsi que je le ferais:

data LuaValue = LuaString String | LuaList [LuaValue]
data Binding = Binding String LuaValue

Cela permet à des valeurs être arbitrairement imbriquées, non seulement imbriqué deux niveaux de profondeur comme le vôtre a. Ensuite, écrivez:

luaValue :: GenParser Char st LuaValue
luaValue = (LuaString <$> identifier)
       <|> (LuaList <$> between (string "{") (string "}") (sepBy (string ",") luaValue))

est l'analyseur pour luaValue. Ensuite, vous avez juste besoin d'écrire:

binding :: GenParser Char st Binding
content :: GenParser Char st [Binding]

Et vous l'aurez. L'utilisation d'un type de données qui représente avec précision ce qui est possible est important.

Autres conseils

En effet, parseurs transmis à l'opérateur de choix doit avoir des types égaux. Vous pouvez dire par le type de l'opérateur de choix:

(<|>) :: GenParser tok st a -> GenParser tok st a -> GenParser tok st a

dit qu'il se fera un plaisir de combiner deux parseurs aussi longtemps que leurs types de jeton, les types et les types de résultats État sont les mêmes.

Alors, comment pouvons-nous assurer que ces parseurs que vous essayez de combiner le même type de résultat? Eh bien, vous avez déjà un Variable de type de données qui capture les différentes formes de variables qui peuvent apparaître dans Lua, donc ce que nous devons faire est de ne pas retourner String, [String] ou [[String]] mais seulement Variables.

Mais quand nous essayons que nous courons un problème. Nous ne pouvons pas laisser encore nestList etc. Variables de retour parce que les constructeurs de Variable exigent des noms de variables et nous ne savons pas encore ceux à ce moment-là. Il existe des solutions de contournement pour cela (comme le retour une String -> Variable de fonction qui attend encore ce nom variable) mais il y a une meilleure solution:. Séparer le nom de la variable des différents types de valeurs qu'une variable peut avoir

data Variable = Variable String Value
  deriving Show

data Value = LuaString String
           | LuaList [Value]
           deriving (Show)

Notez que j'ai supprimé le constructeur de NestedLuaList. J'ai changé LuaList d'accepter une liste de Values plutôt que Strings, donc une liste imbriquée peut maintenant être exprimée en LuaList de LuaLists. Cela permet à des listes d'être imbriquées arbitrairement en profondeur plutôt que seulement deux niveaux comme dans votre exemple. Je ne sais pas si cela est autorisé dans Lua, mais il a fait écrire les parseurs plus facile. : -)

Maintenant, nous pouvons laisser lLists de nestList et de retour de Value:

lList :: GenParser Char st Value
lList = do
  ss <- between (string "{") (string "}") (sepBy varContent (string ","))
  return (LuaList (map LuaString ss))

nestList :: GenParser Char st Value
nestList = do
  vs <- between (string "{") (string "}") (sepBy lList (string ","))
  return (LuaList vs)

Et varName, que je l'ai renommé variable ici, retourne maintenant un Variable:

variable :: GenParser Char st Variable
variable = do
  vName <- (many letter)
  eq <- string " = "
  vCon <- try nestList
       <|> try lList 
       <|> (do v <- varContent; return (LuaString v))
  return (Variable vName vCon)

Je pense que vous constaterez que lorsque vous exécutez votre analyseur sur une entrée il y a encore quelques problèmes, mais vous avez déjà beaucoup plus proche de la solution maintenant qu'auparavant.

J'espère que cela aide!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top