2012-05-25 5 views
1

다소 복잡한 데이터 파일 형식을 처리하기 위해 Parsec 파서에서 작업하고 있습니다.이 형식을 제어 할 수 없습니다.int를 처리하기위한 Parsec lookahead

나는 많은 진전을 이루었지만, 현재 다음과 같이 붙어 있습니다.

나는 다음과 같이 약간 줄을 구문 분석 할 수 있어야합니다 :

4 0.123 1.452 0.667 * 3.460 149 - - 

의미 적의 4가 NODENUM의 Floats* 부정적인 로그 확률입니다이다 (그래서, *이의 부정적인 로그를 나타냅니다 확률 0). 149과 빼기 기호는 정말 쓸모 없기 때문에 폐기 할 수는 있지만 최소한 파서를 깨지 말아야합니다.

가 여기에 지금까지이 작업은 다음과 같습니다

이는 "쓰레기"처리 내가 언급했다. 아마도 더 간단 할 수도 있지만, 그 자체로 작동합니다.

emAnnotationSet = (,,) <$> p_int <*> 
          (reqSpaces *> char '-') <*> 
          (reqSpaces *> char '-') 

라인의 시작 부분에 nodeNum는 작동 다른 파서에 의해 처리되고 난에 얻을 필요가있다.

emAnnotationSet의 시작 부분에 숫자를 사용하지 않고 모든 p_logProb을 줄에서 꺼내는 데 문제가 있습니다.

p_logProb에 대한 파서는 다음과 같습니다

p_logProb = liftA mkScore (lp <?> "logProb") 
      where lp = try dub <|> string "*" 
       dub = (++) <$> ((++) <$> many1 digit <*> string ".") <*> many1 digit 

그리고 다음과 같이 마지막으로, 나는 (정수로 시작) 후행 emAnnotationSet에서 logProb 항목을 구분하는 시도를 :

hmmMatchEmissions  = optSpaces *> (V.fromList <$> sepBy p_logProb reqSpaces) 
         <* optSpaces <* emAnnotationSet <* eol 
         <?> "matchEmissions" 

따라서 p_logProb은 숫자로 시작하고 소수점을 포함하고 추가 숫자가있는 부동 소수점에서만 성공합니다 (이 제한은 파일 형식으로 간주됩니다).

p_logProb 정의의 try은 10 진수와 나머지를 구문 분석하지 않은 경우 선행 숫자를 사용하지 않기를 기대했지만이 방법은 효과가없는 것 같습니다. 파섹은 여전히이 emAnnotationSet에서 그 정수의 숫자 후 예상치 못한 공간을보고 불평 : (196)가 마이너스 기호 앞의 정수 후 공간에 해당

Left "hmmNode" (line 1, column 196): 
unexpected " " 
expecting logProb 

열 때문에 문제가 있다는 것을 나에게 분명 파서가 정수를 사용 중입니다. p_logProb 파서가 lookahead를 올바르게 사용하므로 emAnnotationSet 파서에 대한 입력을 남겨 두도록이 문제를 해결할 수 있습니까?

답변

2

확률을 끝내는 정수는 소수점을 포함하지 않으므로 확률로 오인 할 수 없습니다. lexeme 결합자는 파서를 후행 공백을 건너 뛰는 것으로 변환합니다.

import Text.Parsec 
import Text.Parsec.String 
import Data.Char 
import Control.Applicative ((<$>), (<*>), (<$), (<*), (*>)) 

fractional :: Fractional a => Parser a 
fractional = try $ do 
    n <- fromIntegral <$> decimal 
    char '.' 
    f <- foldr (\d f -> (f + fromIntegral (digitToInt d))/10.0) 0.0 <$> many1 digit 
    return $ n + f 

decimal :: Parser Int 
decimal = foldl (\n d -> 10 * n + digitToInt d) 0 <$> many1 digit 

lexeme :: Parser a -> Parser a 
lexeme p = p <* skipMany (char ' ') 

data Row = Row Int [Maybe Double] 
      deriving (Show) 

probability :: Fractional a => Parser (Maybe a) 
probability = (Just <$> fractional) <|> (Nothing <$ char '*') 

junk = lexeme decimal <* count 2 (lexeme $ char '-') 

row :: Parser Row 
row = Row <$> lexeme decimal <*> many1 (lexeme probability) <* junk 

rows :: Parser [Row] 
rows = spaces *> sepEndBy row (lexeme newline) <* eof 

용도 :

*Main> parseTest rows "4 0.123 1.234 2.345 149 - -\n5 0.123 * 2.345 149 - -" 
[Row 4 [Just 0.123,Just 1.234,Just 2.345],Row 5 [Just 0.123,Nothing,Just 2.345]] 
+0

나는 내가 필요한 것에 이것을 줄 수 있다고 생각한다. 그 별표는 실제로 아무것도가되어서는 안됩니다. 그들과 복식은 실제로 점수의 인스턴스가되지만, 나는 그것들을위한 생성자를 가지므로 사소한 변화가되어야한다. 감사! –

1

나는 당신의 문제를 정확하게 모르겠습니다. 그러나 설명에 따라 주어진 줄을 구문 분석하려면 Text.Parsec.Token1에 정의 된 기존 렉서를 사용하는 것이 훨씬 쉽고 함께 결합하십시오.

아래 코드는 선을 Line 데이터 형식으로 구문 분석하므로 필요에 따라 더 멀리 처리 할 수 ​​있습니다.구문 분석 전에 - 및 정수를 필터링하려고 시도하는 대신 parseEntry 파서를 사용하여 Float 값인 경우 Just Double, 의 경우 *Nothing 인 경우 정수 및 대시를 반환합니다. 이것은 catMaybes을 사용하여 매우 간단하게 필터링됩니다. 오히려

*Test> parseTest parseLine "4 0.123 1.452 0.667 * 3.460 149 - -" 
Line {lineNodeNum = 4, negativeLogProbabilities = [0.123,1.452,0.667,0.0,3.46]} 

유일한 문제 (또는되지 않을 수 있음) 그것을 *- 개의 서로 다른 토큰을 파싱하는 문제 일 수있다 :

module Test where 
import Text.Parsec 
import qualified Text.Parsec.Token as P 
import Text.Parsec.Language (haskellDef) 
import Control.Applicative ((<$>)) 
import Data.Maybe (catMaybes) 
lexer = P.makeTokenParser haskellDef 

parseFloat = P.float lexer 
parseInteger = P.natural lexer 

whiteSpace = P.whiteSpace lexer 

parseEntry = try (Just <$> parseFloat) 
      <|> try (const (Just 0) <$> (char '*' >> whiteSpace)) 
      <|> try (const Nothing <$> (char '-' >> whiteSpace)) 
      <|> (const Nothing <$> parseInteger) 


data Line = Line { 
     lineNodeNum :: Integer 
    , negativeLogProbabilities :: [Double] 
    } deriving (Show) 

parseLine = do 
    nodeNum <- parseInteger 
    whiteSpace 
    probabilities <- catMaybes <$> many1 parseEntry 
    return $ Line { lineNodeNum = nodeNum, negativeLogProbabilities = probabilities } 

사용 예 : 여기

는 코드 파싱시 실패합니다. 예

*Test> parseTest parseLine "4 0.123 1.452 0.667 * 3.460 149 - -*" 
Line {lineNodeNum = 4, negativeLogProbabilities = [0.123,1.452,0.667,0.0,3.46,0.0]} 

참고 로그 확률의 끝에 추가 0.0.

+0

흠 ... 문제이며, '*'0.0로서 해석되어서는 안된다; _negative log_가 0이므로 실제로는 무한대입니다 ('negLogZero'로 처리됨). 그리고 목록의 끝에서 관계없는 "0.0"을 제거하는 것은 정말 불행한 것 같습니다.이 파일 형식의 수레는 실제로 임의의 수레가 아니기 때문에 기존의 렉서를 사용하는 것에 대해서도 신경을 쓰지 않습니다 (예 : 음수 및 전자 표기법은 허용되지 않습니다). –

+0

정말, 십진법 표기법을 사용하여 '*'또는 부동 소수점으로 된 토큰을 하나의 정수 (그리고 두 개의 빼기 부호) 뒤에 파싱해야합니다. 그게 내가 붙어있는 곳이야. –

+0

아, 나는 여분 0.0에 관하여 당신의 요점을 오해했다. 아니, 나는 '- *'에 대해 걱정하지 않는다. 나는 아직도 렉서 사용에 대해 걱정하고있다. 나는 허용 된 double과'*'의리스트에 임의의 int를 허용하고 싶지 않다. 명확히하기 위해 int와 minus 기호는 항상'eol' 바로 앞에있는 줄 끝 부분에 있습니다. –