2012-08-28 11 views
6

Estoy trabajando en la separación de las etapas de lectura y análisis de un analizador. Después de algunas pruebas, me di cuenta de que los mensajes de error son menos útiles cuando uso tokens que no sean los tokens Char de Parsec.Haskell Parsec - los mensajes de error son menos útiles al usar tokens personalizados

Éstos son algunos ejemplos de mensajes de error de Parsec durante el uso de fichas de Char:

ghci> P.parseTest (string "asdf" >> spaces >> string "ok") "asdf wrong" 
parse error at (line 1, column 7): 
unexpected "w" 
expecting space or "ok" 


ghci> P.parseTest (choice [string "ok", string "nop"]) "wrong" 
parse error at (line 1, column 1): 
unexpected "w" 
expecting "ok" or "nop" 

Así, analizador de cadena muestra lo que se espera de cadena cuando se encuentra una cadena inesperada, y la elección analizador muestra cuáles son las alternativas.

Pero cuando se utiliza mismas combinadores con mis fichas:

ghci> Parser.parseTest ((tok $ Ide "asdf") >> (tok $ Ide "ok")) "asdf " 
parse error at "test" (line 1, column 1): 
unexpected end of input 

En este caso, no se imprime lo que se esperaba.

ghci> Parser.parseTest (choice [tok $ Ide "ok", tok $ Ide "nop"]) "asdf " 
parse error at (line 1, column 1): 
unexpected (Ide "asdf","test" (line 1, column 1)) 

Y cuando uso choice, no se imprime alternativas.

Espero que este comportamiento esté relacionado con las funciones del combinador, y no con tokens, pero parece que estoy equivocado. ¿Cómo puedo arreglar esto?

Aquí está el código léxico + analizador completo:

lexer:

module Lexer 
    (Token(..) 
    , TokenPos(..) 
    , tokenize 
    ) where 

import Text.ParserCombinators.Parsec hiding (token, tokens) 
import Control.Applicative ((<*), (*>), (<$>), (<*>)) 

data Token = Ide String 
      | Number String 
      | Bool String 
      | LBrack 
      | RBrack 
      | LBrace 
      | RBrace 
      | Keyword String 
    deriving (Show, Eq) 

type TokenPos = (Token, SourcePos) 

ide :: Parser TokenPos 
ide = do 
    pos <- getPosition 
    fc <- oneOf firstChar 
    r <- optionMaybe (many $ oneOf rest) 
    spaces 
    return $ flip (,) pos $ case r of 
       Nothing -> Ide [fc] 
       Just s -> Ide $ [fc] ++ s 
    where firstChar = ['A'..'Z'] ++ ['a'..'z'] ++ "_" 
     rest  = firstChar ++ ['0'..'9'] 

parsePos p = (,) <$> p <*> getPosition 

lbrack = parsePos $ char '[' >> return LBrack 
rbrack = parsePos $ char ']' >> return RBrack 
lbrace = parsePos $ char '{' >> return LBrace 
rbrace = parsePos $ char '}' >> return RBrace 


token = choice 
    [ ide 
    , lbrack 
    , rbrack 
    , lbrace 
    , rbrace 
    ] 

tokens = spaces *> many (token <* spaces) 

tokenize :: SourceName -> String -> Either ParseError [TokenPos] 
tokenize = runParser tokens() 

Analizador:

module Parser where 

import Text.Parsec as P 
import Control.Monad.Identity 
import Lexer 

parseTest :: Show a => Parsec [TokenPos]() a -> String -> IO() 
parseTest p s = 
    case tokenize "test" s of 
     Left e -> putStrLn $ show e 
     Right ts' -> P.parseTest p ts' 

tok :: Token -> ParsecT [TokenPos]() Identity Token 
tok t = token show snd test 
    where test (t', _) = case t == t' of 
          False -> Nothing 
          True -> Just t 

SOLUCIÓN:

Ok, después de la respuesta de fp4me y la lectura fuente de Char de Parsec más cuidadosamente, terminé con esto:

{-# LANGUAGE FlexibleContexts #-} 
module Parser where 

import Text.Parsec as P 
import Control.Monad.Identity 
import Lexer 

parseTest :: Show a => Parsec [TokenPos]() a -> String -> IO() 
parseTest p s = 
    case tokenize "test" s of 
     Left e -> putStrLn $ show e 
     Right ts' -> P.parseTest p ts' 


type Parser a = Parsec [TokenPos]() a 

advance :: SourcePos -> t -> [TokenPos] -> SourcePos 
advance _ _ ((_, pos) : _) = pos 
advance pos _ [] = pos 

satisfy :: (TokenPos -> Bool) -> Parser Token 
satisfy f = tokenPrim show 
         advance 
         (\c -> if f c then Just (fst c) else Nothing) 

tok :: Token -> ParsecT [TokenPos]() Identity Token 
tok t = (Parser.satisfy $ (== t) . fst) <?> show t 

Ahora que estoy recibiendo mismos mensajes de error:

ghci> Parser.parseTest (opción [tok $ Ide "ok", tok $ Ide "nop"]) "asdf"
de análisis error al (línea 1, columna 1):
inesperado ("asdf" Ide, "test" (línea 1, columna 3))
esperando Ide "ok" o Ide "nop"

+1

Por qué ¿Quieres separar lexing del análisis? Seguramente la razón principal para hacer esto es la tradición: era más simple escribir un analizador complejo libre de los detalles de implementación del lexer (que era más rutinario, tal vez solo expresiones regulares), y en lenguajes imperativos, hace que sea más fácil separar el etapas.En un buen terreno de Haskell Parsec, escribir los lexers y los analizadores sintácticos es agradable y fácil: lee algunas cuerdas, combínalos para analizarlos, casi puedes escribir la definición de tu lenguaje en combinators. Además, estás trabajando duro para pasar posiciones; deja que Parsec lo haga. – AndrewC

+0

@AndrewC, puede que tenga razón. Solo quería ver las partes buenas y malas de separar las etapas de lectura y análisis en parsec. Ahora, después de ver mi código final, creo que voy a usar solo el analizador. (también, una vez que estaba usando alex + happy para analizar una gramática basada en sangría y el lexing me ayudó a generar tokens sangría + dedent y a dejar que el analizador trabaje en gramática simplificada. La etapa lexing separada en parsec también podría ayudar en este tipo de situaciones) – sinan

+0

@AndrewC, también, realmente me encanta Parsec y creo que poder trabajar en diferentes tipos de transmisiones (que no sean secuencias de caracteres) puede ser muy útil y escribir un lexer me ayudó a comprender cómo puedo hacerlo. Ahora sé cómo puedo trabajar en cadenas de bytes, por ejemplo. – sinan

Respuesta

5

Un principio de solución puede ser para definir su ch función oice en el Analizador, utilizar una función inesperada específica para anular error inesperado y finalmente utilizar el operador <?> para anular el mensaje esperando:

mychoice [] = mzero 
mychoice (x:[]) = (tok x <|> myUnexpected) <?> show x 
mychoice (x:xs) = ((tok x <|> mychoice xs) <|> myUnexpected) <?> show (x:xs) 

myUnexpected = do 
      input <- getInput 
      unexpected $ (id $ first input) 
      where 
      first [] = "eof" 
      first (x:xs) = show $ fst x 

y llame a su programa de análisis de esa manera:

ghci> Parser.parseTest (mychoice [Ide "ok", Ide "nop"]) "asdf " 
parse error at (line 1, column 1): 
unexpected Ide "asdf" 
expecting [Ide "ok",Ide "nop"] 
+1

Gracias. Agregué mi código final a la pregunta. – sinan

Cuestiones relacionadas