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"
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
@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
@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