2012-08-28 20 views
6

Bir ayrıştırıcının lexing ve ayrıştırma aşamalarını ayırmak için çalışıyorum. Bazı testlerden sonra, Parsec'in Char token'larından başka bazı belirteçler kullandığımda hata mesajlarının daha az yardımcı olduğunu anladım.Haskell Parsec - hata mesajları, özel jetonları kullanırken daha az kullanışlıdır

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" 

Yani, dize ayrıştırıcı beklenmedik bir dize bulundu ve seçim ayrıştırıcı alternatifleri nelerdir gösterdiğinde dize ne beklendiğini gösterir: Char kullanarak belirteçleri sürerken burada

parsekten en hata iletileri bazı örneklerdir.

aynı benim jeton combinators kullandığınızda Ama: Bu durumda

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

, bu ne bekleniyordu yazdırmaz.

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)) 

Ve choice kullandığınızda, bu alternatifler yazdırmaz.

Bu davranışın, belirteçlerle değil, birleştirici işlevlerle ilgili olmasını bekledim, ancak yanılıyormuşum gibi görünüyor. Bunu nasıl düzeltebilirim?

Lexer:

İşte tam lexer + ayrıştırıcı kod

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() 

Ayrıştırıcı:

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 

ÇÖZÜM:

Tamam, fp4me cevabı ve parsekten en Char kaynağını okuduktan sonra daha dikkatli, ben bu ile bitti:

{-# 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 

Şimdi alıyorum aynı hata iletileri:

GHCi> Parser.parseTest (seçim [$ Ide tok "Tamam", tok $ İde "nop"]) "asdf"
ayrıştırma
beklenmedik (İde "asdf", "test" (satır 1, sütun 3)) Ide bekliyor
"Tamam" veya Ide "nop"

+1

Neden lexing'i ayrıştırmadan ayırmak ister misin? Elbette bunu yapmanın asıl nedeni gelenek - lexer'ın (daha rutin, belki de sadece normal ifadeler olan) uygulama detaylarından arınmış bir ayrıştırıcı yazmanın daha kolay olması ve zorunlu dillerde, evreleri.Güzel Haskell Parsec arazisinde, lexer'ları ve ayrıştırıcıları yazmak güzel ve kolaydır: Lex bazı dizeler, onları ayrıştırmak için onları birleştirir - neredeyse dilinizin tanımını kombinatorlerde yazabilirsiniz. Ayrıca, pozisyonları geçmek için çok çalışıyorsunuz; Parsec'in yapmasına izin ver. – AndrewC

+0

@AndrewC, haklı olabilirsiniz. Sadece parsec'deki lexing ve ayrıştırma aşamalarını ayırmanın iyi ve kötü kısımlarını görmek istedim. Şimdi son kodumu görünce, sadece ayrıştırıcı ile gideceğim düşünüyorum. (Ayrıca, ben bir giriş-temelli dilbilgisi ve lexing ayrıştırmak için alex + mutlu bir kez kullanıldıktan sonra ben girinti + adanmış belirteçleri oluşturmak için yardımcı oldu ve ayrıştırıcı basitleştirilmiş dilbilgisi üzerinde çalışmasına izin verin. parsec ayrı lexing sahne de bu tür durumlarda yardımcı olabilir – sinan

+0

@AndrewC, ayrıca, Parsec'i gerçekten çok seviyorum ve farklı türdeki akışlarda (karakter akışları dışında) çalışabilmenin gerçekten yararlı olabileceğini ve bir lexer yazmanın bana bunu nasıl yapabildiğimi anlama konusunda yardımcı olduğunu düşünüyorum. Şimdi biliyorum bayt dizeleri üzerinde nasıl çalışabilirim, örneğin. – sinan

cevap

5

bir başlangıç: (satır 1, sütun 1) de hata Çözümü ch tanımlamak olabilir Ayrıştırıcı Ses Bildirim fonksiyonu, beklenmeyen bir hata geçersiz kılmak için belirli bir beklenmedik işlevini kullanın ve nihayet bekliyor mesajı geçersiz kılmak için <?> operatörünü kullanın:

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 

ve o şekilde ayrıştırıcı çağırır:

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

Teşekkürler. Son kodumu sorgulamaya ekledim. – sinan

İlgili konular