2012-04-16 3 views
1

"청크"목록으로 구문 분석 할 문자열이 있습니다. 내 문자열이haskell에서 문자열 구문 분석하기

"some text [[anchor]] some more text, [[another anchor]]. An isolated [" 

처럼 그리고 나는 다시 내가 함수와 내가 무엇을해야 유형을 작성하는 관리했습니다이

[ 
    TextChunk "some text ", 
    Anchor "anchor", 
    TextChunk " some more text, " 
    Anchor "another anchor", 
    TextChunk ". An isolated [" 
] 

뭔가를 얻을 것으로 예상하지만, 지나치게 추한 것 같다. 더 좋은 방법이 있나요?

data Token = TextChunk String | Anchor String deriving (Show) 
data TokenizerMode = EatString | EatAnchor deriving (Show) 

tokenize::[String] -> [Token] 
tokenize xs = 
    let (_,_,tokens) = tokenize' (EatString, unlines xs, [TextChunk ""]) 
    in reverse tokens 

tokenize' :: (TokenizerMode, String, [Token]) -> (TokenizerMode, String,[Token]) 
-- If we're starting an anchor, add a new anchor and switch modes 
tokenize' (EatString, '[':'[':xs, tokens) = tokenize' (EatIdentifier, xs, (Identifier ""):tokens) 
-- If we're ending an anchor ass a new text chunk and switch modes 
tokenize' (EatAnchor, ']':']':xs, tokens) = tokenize' (EatString, xs, (TextChunk ""):tokens) 
-- Otherwise if we've got stuff to consume append it 
tokenize' (EatString, x:xs, (TextChunk t):tokens) = tokenize'(EatString, xs, (TextChunk (t++[x])):tokens) 
tokenize' (EatAnchor, x:xs, (Identifier t):tokens) = tokenize'(EatAnchor, xs, (Identifier (t++[x])):tokens) 
--If we've got nothing more to consume we're done. 
tokenize' (EatString, [], tokens) = (EatString, [], tokens) 
--We'll only get here if we're given an invalid string 
tokenize' xx = error ("Error parsing .. so far " ++ (show xx)) 
+2

정말 토큰 화가 아니며 구문 분석 중입니다. 파싱 ​​요구 사항에 따라, 파섹. –

+0

@CatPlusPlus는 구문 분석에 일치하는 텍스트와 제목을 업데이트하는 것에 동의했습니다. –

+0

@CatPlusPlus parsec을 사용하여 이것이 어떻게 보이는지 보여 줄 수 있습니까? 나는 내가 좋아하는 것에 대해 약간은 모호한 문서/tutes를 찾는다. –

답변

11

이 고독한 괄호를 포함하여, 작동합니다 :

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

data Text = TextChunk String 
      | Anchor String 
      deriving Show 

chunkChar = noneOf "[" <|> try (char '[' <* notFollowedBy (char '[')) 
chunk  = TextChunk <$> many1 chunkChar 
anchor = Anchor <$> (string "[[" *> many (noneOf "]") <* string "]]") 
content = many (chunk <|> anchor) 

parseS :: String -> Either ParseError [Text] 
parseS input = parse content "" input 

참고 try의 사용은 chunkChar 파서가이 여는 괄호 일치 할 때 역 추적 할 수 있도록. try이 없으면 첫 번째 브래킷이 그 시점에서 소비되었을 것입니다.

4

다음은 두 개의 상호 재귀 적 기능을 사용하는 단순한 버전입니다.

module Tokens where 

data Token = TextChunk String | Anchor String deriving (Show) 

tokenize :: String -> [Token] 
tokenize = textChunk emptyAcc 


textChunk :: Acc -> String -> [Token] 
textChunk acc []   = [TextChunk $ getAcc acc] 
textChunk acc ('[':'[':ss) = TextChunk (getAcc acc) : anchor emptyAcc ss 
textChunk acc (s:ss)  = textChunk (snocAcc acc s) ss 

anchor :: Acc -> String -> [Token] 
anchor acc []    = error $ "Anchor not terminated" 
anchor acc (']':']':ss) = Anchor (getAcc acc) : textChunk emptyAcc ss 
anchor acc (s:ss)   = anchor (snocAcc acc s) ss 


-- This is a Hughes list (also called DList) which allows 
-- efficient 'Snoc' (adding to the right end). 
-- 
type Acc = String -> String 

emptyAcc :: Acc 
emptyAcc = id 

snocAcc :: Acc -> Char -> Acc 
snocAcc acc c = acc . (c:) 

getAcc :: Acc -> String 
getAcc acc = acc [] 

이 버전은 입력 텍스트에 두 개의 연속 앵커가있는 경우 시작 또는 앵커로 끝나는 경우 또는 비어 TextChunks를 생성하는 문제가있다.

축적이 비어있는 경우 TextChunk을 생성하지 검사를 추가 할 직선적이지만 약 두 배 긴 코드를 만든다 - 어쩌면 내가 결국 파섹 도달 할 ... 모나드를 사용

+0

빈 TextChunks에 관심이 있다면 빈 TextChunks를 쉽게 포스트 프로세스로 걸러 낼 수있었습니다. –

+0

목록에 추가에 대한 성능 포인터를 주셔서 감사 드리며 DList가 작동합니다. –

1

솔루션 파섹.

import Text.ParserCombinators.Parsec 

data Text = TextChunk String 
      | Anchor String 
      deriving Show 

inputString = "some text [[anchor]] some more text, [[another anchor]]." 

content :: GenParser Char st [Text] 
content = do 
    s1 <- many (noneOf "[") 
    string "[[" 
    s2 <- many (noneOf "]") 
    string "]]" 
    s3 <- many (noneOf "[") 
    string "[[" 
    s4 <- many (noneOf "]") 
    string "]]." 
    return $ [TextChunk s1, Anchor s2, TextChunk s3, Anchor s4] 


parseS :: String -> Either ParseError [Text] 
parseS input = parse content "" input 

은 어떻게 작동 :

> parseS inputString 
Right [TextChunk "some text ",Anchor "anchor",TextChunk " some more text, ",Anchor "another anchor"] 
it :: Either ParseError [Text] 
+2

더 일반적으로,'chunk = TextChunk <$> many1 (noneOf "[")'및'anchor = Anchor <$> (string [[ "* * many (noneOf"]) 앵커로'content = many (덩어리 <|> 앵커) ") <* string">] ")'('Control.Applicative'의 단축키를 사용합니다). 그것은 텍스트 청크와 앵커의 조합으로 작동해야합니다. – hammar

+0

@ hammar, 거의 괜찮습니다.하지만 텍스트에서 '['를 허용하지 않습니다. 이를 예제 문자열에 추가하여 "[[stuff]]"를 앵커로 취급하고 텍스트 청크에 갇혀있는 모든 것을 처리하기를 원합니다. –

관련 문제