Неожиданный конец ввода в парсек - PullRequest
5 голосов
/ 04 июня 2011

Я хочу разобрать файл следующим образом:

66:3 3:4
329:2 
101:3 
495:4 
55:5 
268:5 
267:2 
242:4 
262:1 
861:1 

Мой код похож на следующий:

getTestData :: String -> IO [[(Int, Int)]]
getTestData name = do
    --res <- parseFromFile testData (name ++ ".test")
    fc <- readFile (name ++ ".test")
    let res = parse testData "test data" fc
    case res of
        Left e -> error $ show e-- "test data parse eror."
        Right ts -> return ts

eol = char '\n'
testData = endBy line eol
--testData = many line
testTuple = do
    i <- natural
    colon
    r <- natural
    return (fromIntegral i:: Int, fromIntegral r:: Int)

line = sepBy testTuple whiteSpace

Но при запуске выдается исключение:

ts <- getTestData "data" 
*** Exception: "test data" (line 11, column 1):
unexpected end of input
expecting natural or "\n"

Я не понимаю, почему он сказал строку 11, когда мой файл data.test содержит только 10 строк.Поэтому я не смог решить эту проблему после нескольких попыток.

Ответы [ 4 ]

5 голосов
/ 04 июня 2011

Мое лучшее предположение состоит в том, что whiteSpace в line потребляет символы новой строки.Таким образом, весь ваш файл анализируется одним парсером line, и парсер eol никогда не получит в свои руки "\n".Попробуйте заменить whiteSpace на many (char ' ') и посмотрите, поможет ли это.

4 голосов
/ 04 июня 2011

Это рабочая реализация, использующая примитивные парсеры символов, а не парсеры токенов. Обратите внимание - более надежно не использовать пробел в качестве разделителя, а отбросить его, если он существует. Биты, в которых я использовал однострочную запись, намного точнее, если вы используете (<*) из Applicative.

{-# OPTIONS -Wall #-}

module ParsecWhite where

import Text.ParserCombinators.Parsec

import Data.Char

main = getTestData "sample"

getTestData :: String -> IO [[(Int, Int)]]
getTestData name = do
    --res <- parseFromFile testData (name ++ ".test")
    fc <- readFile (name ++ ".test")
    let res = parse testData "test data" fc
    case res of
        Left e -> error $ show e -- "test data parse eror."
        Right ts -> return ts

testData :: Parser [[(Int,Int)]]
testData = input


input :: Parser [[(Int,Int)]]
input = many (do { a <- line; newline; return a })
     <?> "input"

line :: Parser [(Int,Int)]
line = many (do { a <- testTuple; softWhite; return a})  <?> "line"

testTuple :: Parser (Int,Int)
testTuple = do
    i <- natural
    colon
    r <- natural
    return (i,r)
  <?> "testTuple"

softWhite :: Parser ()
softWhite = many (oneOf " \t") >> return ()

colon :: Parser () 
colon = char ':' >> return ()

natural :: Parser Int
natural = fmap (post 0) $ many1 digit
  where
    post ac []     = (ac * 10) 
    post ac [x]    = (ac * 10) + digitToInt x
    post ac (x:xs) = post ((ac * 10) + digitToInt x) xs
1 голос
/ 04 июня 2011

Могу поспорить, что вам не хватает новой строки в конце последней строки. Для разбора полной строки это должно быть "861: 1 \ n", но, вероятно, это "861: 1EOF". Поэтому я думаю, что ваш синтаксический анализатор правильно определяет ваш ввод, чтобы быть неверным.

0 голосов
/ 04 октября 2013

На самом деле, я обнаружил, что вы можете использовать whiteSpace (например, чтобы легко игнорировать многострочные комментарии к блоку), оставаясь при этом ориентированным на строки.просто включите этот синтаксический анализатор, когда вы хотите новые строки.

col (== 1) "only matches beginning of line"

col pred errStr = do
  c <- sourceColumn <$> getPosition
  if pred c then return ()
            else unexpected errStr
...