2012-08-28 38 views
6

我正在分離分析器的lexing和分析階段。經過一些測試後,我意識到錯誤消息在我使用除Parsec的Char標記以外的某些標記時幫助不大。Haskell Parsec - 錯誤消息在使用自定義令牌時不太有用

這裏是秒差距的錯誤消息的一些例子,而使用字符標記:

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" 

所以,串分析器顯示了發現一個意外的字符串時,和選擇解析器說明了什麼是替代字符串的預期。

但是,當我用我的令牌相同的組合子:

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

在這種情況下,不打印什麼預期。

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

而當我使用choice時,它不打印替代品。

我希望這種行爲與combinator函數有關,而不是與令牌,但似乎我錯了。我怎樣才能解決這個問題?

下面是完整的詞法分析器+解析器代碼:

詞法:

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

分析器:

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 

SOLUTION:

好吧,以後fp4me的答案和閱讀秒差距的字符源更仔細,我結束了這個:

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

現在我得到同樣的錯誤信息:

ghci中> Parser.parseTest(選擇[TOK $ IDE 「OK」,TOK $ IDE 「NOP」]) 「ASDF」
解析誤差在(第1行,第1列):
意外(IDE 「ASDF」, 「測試」(第1行,第3列))
期待Ide的 「OK」 或IDE 「NOP」

+1

爲什麼你想分離lexing從解析?這樣做的主要原因是傳統 - 編寫一個棘手的解析器比詞法分析器的實現細節更簡單(這是更常規的,也許只是正則表達式),並且在命令式語言中,它使得思考更容易分離階段。在好的Haskell Parsec土地上,編寫詞法分析器和解析器非常簡單:使用一些字符串,將它們組合起來解析它們 - 您幾乎可以在組合器中編寫語言的定義。此外,你正在努力通過職位,讓Parsec去做吧。 – AndrewC

+0

@AndrewC,你可能是對的。我只是想看看parsec中分離lexing和解析階段的好處和壞處。現在看了我的最終代碼之後,我想我會只用解析器。 (另外,一旦我使用alex + happy來解析基於縮進的語法,lexing幫助我生成縮進+縮進符號,並讓解析器工作在簡化語法上。parsec中單獨的lexing階段也可以幫助解決這種情況) – sinan

+0

@AndrewC,我也非常喜歡Parsec,我認爲能夠處理不同類型的流(字符流除外)可以非常有幫助,編寫一個詞法分析器幫助我理解我該怎麼做。例如,現在我知道如何處理字節字符串。 – sinan

回答

5

一個開端的解決方案可以定義你的通道在解析器音色功能, 使用特定意想不到的功能覆蓋意外的錯誤,最後 使用<?>運算符重載期待消息:

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 

,並調用解析器這樣的:

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

謝謝。我添加了我的最終代碼來提問。 – sinan