का उपयोग करते समय त्रुटि संदेश कम उपयोगी होते हैं, मैं एक पार्सर के अलग-अलग लेक्सिंग और पार्सिंग चरणों पर काम कर रहा हूं। कुछ परीक्षणों के बाद, मुझे एहसास हुआ कि जब मैं पारसेक के चार टोकन के अलावा कुछ टोकन का उपयोग कर रहा हूं तो त्रुटि संदेश कम उपयोगी होते हैं।हास्केल पारसेक - कस्टम टोकन
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"
क्यों क्या आप पार्सिंग से लेक्सिंग अलग करना चाहते हैं? निश्चित रूप से ऐसा करने का मुख्य कारण परंपरा है - लेक्सर के कार्यान्वयन विवरण से मुक्त एक कठिन पार्सर लिखना आसान था (जो अधिक नियमित, शायद केवल नियमित अभिव्यक्ति थी), और अनिवार्य भाषाओं में, यह विचार को अलग करना आसान बनाता है चरणों।अच्छी हास्केल पारसीक भूमि में, लेक्सर्स और पार्सर्स लिखना अच्छा और आसान है: कुछ तारों को लेक्स करें, उन्हें पार्स करने के लिए उन्हें गठबंधन करें - आप लगभग संयोजकों में अपनी भाषा की परिभाषा लिख सकते हैं। इसके अलावा, आप पदों को पारित करने के लिए कड़ी मेहनत कर रहे हैं; पारसेक इसे करने दें। – AndrewC
@AndrewC, आप सही हो सकते हैं। मैं सिर्फ पारसी में लेक्सिंग और पार्सिंग चरणों को अलग करने के अच्छे और बुरे हिस्सों को देखना चाहता था। अब जब मैं अपना अंतिम कोड देखता हूं, तो मुझे लगता है कि मैं सिर्फ पार्सर के साथ जाऊंगा। (एक बार, जब मैं एलेक्स का उपयोग कर रहा था + इंडेंटेशन-आधारित व्याकरण और लेक्सिंग को पार्स करने में खुशी हुई तो मुझे इंडेंट + समर्पित टोकन उत्पन्न करने में मदद मिली, और पार्सर सरलीकृत व्याकरण पर काम करने दें। इस प्रकार की स्थितियों में अलग-अलग लेक्सिंग चरण भी मदद कर सकता है) – sinan
@ एंड्रयूसी, मैं भी वास्तव में पारसेक से प्यार करता हूं और मुझे लगता है कि विभिन्न प्रकार की धाराओं (चरित्र धाराओं के अलावा) पर काम करने में सक्षम होना वास्तव में सहायक हो सकता है और एक लेक्सर लिखने से मुझे यह समझने में मदद मिली कि मैं यह कैसे कर सकता हूं। अब मुझे पता है कि मैं बाइट तारों पर कैसे काम कर सकता हूं, उदाहरण के लिए। – sinan