2010-06-11 6 views
14

Haskell Je suis en train d'analyser un langage basé sur indentation (pensez Python, Haskell lui-même, Boo, YAML) en Haskell utilisant parsec. Je l'ai vu la bibliothèque IndentParser, et il semble que c'est le match parfait, mais ce que je ne peux pas comprendre comment faire mon TokenParser dans un analyseur d'indentation. Voici le code que j'ai jusqu'à présent:syntaxes à base Indentation d'analyse syntaxique parsec

import qualified Text.ParserCombinators.Parsec.Token as T 
import qualified Text.ParserCombinators.Parsec.IndentParser.Token as IT 

lexer = T.makeTokenParser mylangDef 
ident = IT.identifier lexer 

Cela jette l'erreur:

parser2.hs:29:28: 
    Couldn't match expected type `IT.TokenParser st' 
      against inferred type `T.GenTokenParser s u m' 
    In the first argument of `IT.identifier', namely `lexer' 
    In the expression: IT.identifier lexer 
    In the definition of `ident': ident = IT.identifier lexer 

Qu'est-ce que je fais mal? Comment dois-je créer un IT.TokenParser? Ou est IndentParser cassé et à éviter?

Répondre

12

On dirait que vous utilisez parsec 3 ici, alors que IndentParser attend parsec 2. Votre exemple me compile pour avec -package parsec-2.1.0.1.

Donc IndentParser est pas nécessairement cassé, mais l'auteur (s) aurait dû être plus précis sur les versions dans la liste des dépendances. Il est possible d'avoir les deux versions de Parsec installées, donc il n'y a aucune raison de ne pas utiliser IndentParser à moins que vous ne vous soyez engagé à utiliser Parsec 3 pour d'autres raisons.


MISE À JOUR: En fait aucun changement à la source sont nécessaires pour obtenir IdentParser travailler avec parsec 3. Le problème qui nous les deux avaient semble être causé par le fait que cabal-install a une "soft preference" pour parsec 2 . vous pouvez simplement réinstaller IndentParser avec une contrainte explicite sur la version parsec:

cabal install IndentParser --reinstall --constraint="parsec >= 3" 

vous pouvez également télécharger le source et build and install in the normal way.

+0

Vous, monsieur, êtes génial! Je vous remercie! Comment avez-vous su que j'utilisais Parsec 3? Un invité? Parce que je pense que mon exemple pourrait être soit ... – pavpanchekha

+1

J'ai peur que mon travail de détective ici ne soit pas vraiment excitant: j'ai compilé votre code avec Parsec 3, j'ai eu une erreur similaire à la vôtre, puis essayé Parsec 2, travaillé. Au fait, il semble que ce ne serait pas trop compliqué d'utiliser IndentParser avec Parsec 3; vous pourriez envisager de donner un coup de feu si vous trouvez IndentParser utile. –

+0

Je pourrais, mais maintenant je ne fais qu'apprendre Haskell; J'ai peur de me perdre dans une base de code étrangère comme ça. – pavpanchekha

6

est ici un ensemble de combinateurs analyseur j'ai mis ensemble pour parsec 3 qui peuvent être utilisés pour la mise en page de style Haskell, qui pourraient être utiles pour vous. Les principales considérations sont que laidout démarre et exécute une règle de mise en page, et que vous devez utiliser les space et spaced combinators fournies plutôt que le stock Parsec combinators dans le même but. En raison de l'interaction de la mise en page et des commentaires, j'ai dû fusionner le commentaire d'analyse dans le tokenizer.

{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses #-} 
module Text.Parsec.Layout 
    (laidout   -- repeat a parser in layout, separated by (virtual) semicolons 
    , space   -- consumes one or more spaces, comments, and onside newlines in a layout rule 
    , maybeFollowedBy 
    , spaced   -- (`maybeFollowedBy` space) 
    , LayoutEnv  -- type needed to describe parsers 
    , defaultLayoutEnv -- a fresh layout 
    , semi    -- semicolon or virtual semicolon 
    ) where 

import Control.Applicative ((<$>)) 
import Control.Monad (guard) 

import Data.Char (isSpace) 

import Text.Parsec.Combinator 
import Text.Parsec.Pos 
import Text.Parsec.Prim hiding (State) 
import Text.Parsec.Char hiding (space) 

data LayoutContext = NoLayout | Layout Int deriving (Eq,Ord,Show) 

data LayoutEnv = Env 
    { envLayout :: [LayoutContext] 
    , envBol :: Bool -- if true, must run offside calculation 
    } 

defaultLayoutEnv :: LayoutEnv 
defaultLayoutEnv = Env [] True 

pushContext :: Stream s m c => LayoutContext -> ParsecT s LayoutEnv m() 
pushContext ctx = modifyState $ \env -> env { envLayout = ctx:envLayout env } 

popContext :: Stream s m c => String -> ParsecT s LayoutEnv m() 
popContext loc = do 
    (_:xs) <- envLayout <$> getState 
    modifyState $ \env' -> env' { envLayout = xs } 
    <|> unexpected ("empty context for " ++ loc) 

getIndentation :: Stream s m c => ParsecT s LayoutEnv m Int 
getIndentation = depth . envLayout <$> getState where 
    depth :: [LayoutContext] -> Int 
    depth (Layout n:_) = n 
    depth _ = 0 

pushCurrentContext :: Stream s m c => ParsecT s LayoutEnv m() 
pushCurrentContext = do 
    indent <- getIndentation 
    col <- sourceColumn <$> getPosition 
    pushContext . Layout $ max (indent+1) col 

maybeFollowedBy :: Stream s m c => ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m a 
t `maybeFollowedBy` x = do t' <- t; optional x; return t' 

spaced :: Stream s m Char => ParsecT s LayoutEnv m a -> ParsecT s LayoutEnv m a 
spaced t = t `maybeFollowedBy` space 

data Layout = VSemi | VBrace | Other Char deriving (Eq,Ord,Show) 

-- TODO: Parse C-style #line pragmas out here 
layout :: Stream s m Char => ParsecT s LayoutEnv m Layout 
layout = try $ do 
    bol <- envBol <$> getState 
    whitespace False (cont bol) 
    where 
    cont :: Stream s m Char => Bool -> Bool -> ParsecT s LayoutEnv m Layout 
    cont True = offside 
    cont False = onside 

    -- TODO: Parse nestable {-# LINE ... #-} pragmas in here 
    whitespace :: Stream s m Char => 
     Bool -> (Bool -> ParsecT s LayoutEnv m Layout) -> ParsecT s LayoutEnv m Layout 
    whitespace x k = 
      try (string "{-" >> nested k >>= whitespace True) 
     <|> try comment 
     <|> do newline; whitespace True offside 
     <|> do tab; whitespace True k 
     <|> do (satisfy isSpace <?> "space"); whitespace True k 
     <|> k x 

    comment :: Stream s m Char => ParsecT s LayoutEnv m Layout 
    comment = do 
     string "--" 
     many (satisfy ('\n'/=)) 
     newline 
     whitespace True offside 

    nested :: Stream s m Char => 
     (Bool -> ParsecT s LayoutEnv m Layout) -> 
     ParsecT s LayoutEnv m (Bool -> ParsecT s LayoutEnv m Layout) 
    nested k = 
      try (do string "-}"; return k) 
     <|> try (do string "{-"; k' <- nested k; nested k') 
     <|> do newline; nested offside 
     <|> do anyChar; nested k 

    offside :: Stream s m Char => Bool -> ParsecT s LayoutEnv m Layout 
    offside x = do 
     p <- getPosition 
     pos <- compare (sourceColumn p) <$> getIndentation 
     case pos of 
      LT -> do 
       popContext "the offside rule" 
       modifyState $ \env -> env { envBol = True } 
       return VBrace 
      EQ -> return VSemi 
      GT -> onside x 

    -- we remained onside. 
    -- If we skipped any comments, or moved to a new line and stayed onside, we return a single a ' ', 
    -- otherwise we provide the next char 
    onside :: Stream s m Char => Bool -> ParsecT s LayoutEnv m Layout 
    onside True = return $ Other ' ' 
    onside False = do 
     modifyState $ \env -> env { envBol = False } 
     Other <$> anyChar 

layoutSatisfies :: Stream s m Char => (Layout -> Bool) -> ParsecT s LayoutEnv m() 
layoutSatisfies p = guard . p =<< layout 

virtual_lbrace :: Stream s m Char => ParsecT s LayoutEnv m() 
virtual_lbrace = pushCurrentContext 

virtual_rbrace :: Stream s m Char => ParsecT s LayoutEnv m() 
virtual_rbrace = try (layoutSatisfies (VBrace ==) <?> "outdent") 

-- recognize a run of one or more spaces including onside carriage returns in layout 
space :: Stream s m Char => ParsecT s LayoutEnv m String 
space = do 
    try $ layoutSatisfies (Other ' ' ==) 
    return " " 
    <?> "space" 

-- recognize a semicolon including a virtual semicolon in layout 
semi :: Stream s m Char => ParsecT s LayoutEnv m String 
semi = do 
    try $ layoutSatisfies p 
    return ";" 
    <?> "semi-colon" 
    where 
     p VSemi = True 
     p (Other ';') = True 
     p _ = False 

lbrace :: Stream s m Char => ParsecT s LayoutEnv m String 
lbrace = do 
    char '{' 
    pushContext NoLayout 
    return "{" 

rbrace :: Stream s m Char => ParsecT s LayoutEnv m String 
rbrace = do 
    char '}' 
    popContext "a right brace" 
    return "}" 

laidout :: Stream s m Char => ParsecT s LayoutEnv m a -> ParsecT s LayoutEnv m [a] 
laidout p = try (braced statements) <|> vbraced statements where 
    braced = between (spaced lbrace) (spaced rbrace) 
    vbraced = between (spaced virtual_lbrace) (spaced virtual_rbrace) 
    statements = p `sepBy` spaced semi 
+0

Pouvez-vous donner un exemple d'utiliser ce? Je l'ai essayé [comme ceci] (https://gist.github.com/gergoerdi/af1829b18ea80e21ba79728a5d271cd9) mais je ne peux pas obtenir 'bindings' pour accepter quelque chose aussi simple que' unlines ["x = y", "a = b "]'. – Cactus

+1

Je pense actuellement que la source ci-dessus est cassée, mais je n'ai pas eu l'occasion de la revisiter. –