2012-02-04 6 views
3

Ici, j'ai à l'esprit qu'une configuration possible est un arbre de spécifications, chaque spécification a un mot-clé correspondant (la chaîne) et le type. Quelque chose comme ceci:Utilisation de Parsec pour analyser les configurations

data Select = And | Or 
data ConfigTree = Node Select [ConfigTree] | Leaf (String, *) 

Je ne sais pas comment écrire correctement ce, étant donné qu'il n'y a pas de « type de types », mais tant pis que pour le moment. Maintenant, étant donné un tel arbre, je veux construire un analyseur qui peut lire une configuration valide possible; Je suppose que j'ai déjà sous-analyseurs qui peuvent analyser les paires mot-clé/type.

Par exemple, un arbre de configuration possible est:

Node And [ Leaf ("width", Double) 
     , Node Or [ Leaf ("height", Double) , Leaf ("aspectratio", Double) 
     ] 

qui peut spécifier la taille d'un rectangle. Un fichier de configuration possible serait, par exemple:

aspectratio = 2 
width = 10 

(Supposons qu'un fichier de configuration est juste une liste de saut de ligne séparés paires, mot-clé = bla, où blah est quelque chose de l'analyseur correspondant pour ce mot clé peut traiter; mais ils peuvent être dans n'importe quel ordre, et doivent juste correspondre avec un possible "sous-ensemble valide" de l'arbre, où un sous-ensemble valide est un sous-ensemble contenant le nœud supérieur, qui contient tous les enfants d'un nœud "et" qu'il contient , et dites exactement un enfant d'un "ou" nœud qu'il contient.)

Je ne sais pas comment même commencer à construire un tel analyseur. Quelqu'un peut-il donner quelques conseils sur la façon de procéder, ou un moyen de restructurer complètement le type de données ConfigTree ci-dessus à quelque chose de plus facile à analyser?

Répondre

2

Le problème avec la construction d'un analyseur pour cela, est que votre format d'entrée ne correspond pas du tout à votre type de données. Le format d'entrée est une liste simple et facilement analysable de paires clé-valeur alors que votre type de données est un arbre. Par exemple. Pour déterminer si tous les sous-arbres d'un nœud And sont valides, vous devez connaître l'entrée complète. Donc, au lieu de faire la validation de la liste des paires clé-valeur directement dans l'analyseur, faites-le simplement après.

Je l'ai mis en place un petit exemple pour montrer ce que je veux dire:

data Type = TDouble | TString 
data Select = And | Or 
data ConfigTree = Node Select [ConfigTree] | Leaf (String, Type) 

-- matches a list of key-value pairs against a tree 
match :: [(String, String)] -> ConfigTree -> Bool 
match sts (Leaf (s, t)) = case filter ((== s) . fst) sts of 
          -- we don't want multiple occurences of a key 
          [(_, v)] -> if valid v t then True else False 
          _  -> False 
match sts (Node And cfgs) = and . map (match sts) $ cfgs 
-- not completely what you described, because it will match 1 or more 
match sts (Node Or cfgs) = or . map (match sts) $ cfgs 

-- validates a string against a type 
valid :: String -> Type -> Bool 
valid s TDouble = case reads s :: [(Double, String)] of 
        [(_, "")] -> True 
        _   -> False 
valid _ TString = True 

-- this is what you actually parsed 
config = [ ("aspectratio", "2") 
     , ("width", "123") 
     , ("name", "Sam") 
     ] 

-- the example tree 
cfgTree = Node And [ Leaf ("width", TDouble) 
        , Node Or [ Leaf ("height", TDouble), Leaf ("aspectratio", TDouble)] 
        ] 

Je ne pense pas que cela est un exemple particulièrement utile, car tout ce qu'il fait est de vérifier si vos données de configuration sont valides , ça ne les extrait pas, mais j'espère que ça démontre ce que je voulais dire.

+0

Merci beaucoup. Je viens juste d'utiliser cette réponse pour faire quelque chose qui transforme une entrée en un arbre de configuration rempli, en donnant une erreur si l'arbre résultant n'est pas valide. Je n'ai pas encore rendu les messages d'erreur aussi utiles que possible (c'est l'une des raisons pour lesquelles je considérais Parsec à la place), mais cela semble fonctionner pour le moment. Merci! –

+0

OK, ma seule vraie question maintenant: y a-t-il une bonne manière de traiter correctement les types, autre que d'énumérer tous les types qui pourraient entrer en jeu? (Je réalise que ce n'est pas vraiment la question, désolé.) –

+0

Peut-être que Data.Dynamic est ce que vous cherchez? Mais je ne pense pas que la liste de tous les types est une si mauvaise idée. Combien de types différents y aura-t-il? Peut-être 3 - Int, Double et String? Btw: Avez-vous regardé s'il y a des paquets sur Hackage qui répondent à vos besoins? – bzn

Questions connexes