2012-08-28 29 views
6

का उपयोग करते समय त्रुटि संदेश कम उपयोगी होते हैं, मैं एक पार्सर के अलग-अलग लेक्सिंग और पार्सिंग चरणों पर काम कर रहा हूं। कुछ परीक्षणों के बाद, मुझे एहसास हुआ कि जब मैं पारसेक के चार टोकन के अलावा कुछ टोकन का उपयोग कर रहा हूं तो त्रुटि संदेश कम उपयोगी होते हैं।हास्केल पारसेक - कस्टम टोकन

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" 

तो, स्ट्रिंग पार्सर पता चलता है कि स्ट्रिंग की उम्मीद है जब एक अप्रत्याशित स्ट्रिंग पाया, और विकल्प पार्सर पता चलता है कि विकल्प हैं:

यहाँ Parsec के त्रुटि संदेशों में से कुछ उदाहरण हैं, जबकि चार का उपयोग कर टोकन हैं।

लेकिन जब मैं अपने टोकन के साथ एक ही combinators का उपयोग करें:

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 उपयोग करें, यह विकल्प मुद्रित नहीं करता है।

मुझे उम्मीद है कि यह व्यवहार संयोजक कार्यों से संबंधित होगा, न कि टोकन के साथ, लेकिन ऐसा लगता है कि मैं गलत हूं। मैं इसे कैसे ठीक करूं?

Lexer:

यहाँ पूर्ण 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() 

पार्सर:

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 

समाधान:

ठीक है, fp4me के जवाब और Parsec के चार स्रोत को पढ़ने के बाद अधिक सावधानी से, मैं इसके साथ समाप्त हुआ:

{-# 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 (विकल्प [टोक $ इदे "ठीक है", टोक $ इदे "nop"]) "asdf"
पार्स
अप्रत्याशित (आईडीई "asdf", "परीक्षण" (लाइन 1, स्तंभ 3))
इदे उम्मीद "ठीक है" या इदे "nop"

+1

क्यों क्या आप पार्सिंग से लेक्सिंग अलग करना चाहते हैं? निश्चित रूप से ऐसा करने का मुख्य कारण परंपरा है - लेक्सर के कार्यान्वयन विवरण से मुक्त एक कठिन पार्सर लिखना आसान था (जो अधिक नियमित, शायद केवल नियमित अभिव्यक्ति थी), और अनिवार्य भाषाओं में, यह विचार को अलग करना आसान बनाता है चरणों।अच्छी हास्केल पारसीक भूमि में, लेक्सर्स और पार्सर्स लिखना अच्छा और आसान है: कुछ तारों को लेक्स करें, उन्हें पार्स करने के लिए उन्हें गठबंधन करें - आप लगभग संयोजकों में अपनी भाषा की परिभाषा लिख ​​सकते हैं। इसके अलावा, आप पदों को पारित करने के लिए कड़ी मेहनत कर रहे हैं; पारसेक इसे करने दें। – AndrewC

+0

@AndrewC, आप सही हो सकते हैं। मैं सिर्फ पारसी में लेक्सिंग और पार्सिंग चरणों को अलग करने के अच्छे और बुरे हिस्सों को देखना चाहता था। अब जब मैं अपना अंतिम कोड देखता हूं, तो मुझे लगता है कि मैं सिर्फ पार्सर के साथ जाऊंगा। (एक बार, जब मैं एलेक्स का उपयोग कर रहा था + इंडेंटेशन-आधारित व्याकरण और लेक्सिंग को पार्स करने में खुशी हुई तो मुझे इंडेंट + समर्पित टोकन उत्पन्न करने में मदद मिली, और पार्सर सरलीकृत व्याकरण पर काम करने दें। इस प्रकार की स्थितियों में अलग-अलग लेक्सिंग चरण भी मदद कर सकता है) – sinan

+0

@ एंड्रयूसी, मैं भी वास्तव में पारसेक से प्यार करता हूं और मुझे लगता है कि विभिन्न प्रकार की धाराओं (चरित्र धाराओं के अलावा) पर काम करने में सक्षम होना वास्तव में सहायक हो सकता है और एक लेक्सर लिखने से मुझे यह समझने में मदद मिली कि मैं यह कैसे कर सकता हूं। अब मुझे पता है कि मैं बाइट तारों पर कैसे काम कर सकता हूं, उदाहरण के लिए। – sinan

उत्तर

5

एक शुरुआत: (लाइन 1, स्तंभ 1) में त्रुटि समाधान का आपके ch को परिभाषित किया जा सकता है पार्सर में oice समारोह, अनपेक्षित त्रुटि ओवरराइड करने के लिए एक विशिष्ट अप्रत्याशित फ़ंक्शन का उपयोग करें और अंत में उम्मीद संदेश ओवरराइड करने के लिए <?> ऑपरेटर का उपयोग करें:

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