6

I want to create a parser combinator, which will collect all lines below current place, which indentation levels will be greater or equal some i. I think the idea is simple:

Consume a line - if its indentation is:

  • ok -> do it for next lines
  • wrong -> fail

Lets consider following code:

import qualified Text.ParserCombinators.UU as UU
import           Text.ParserCombinators.UU hiding(parse)
import           Text.ParserCombinators.UU.BasicInstances hiding (Parser)

-- end of line
pEOL   = pSym '\n'

pSpace = pSym ' '
pTab   = pSym '\t'

indentOf s = case s of
    ' '  -> 1
    '\t' -> 4

-- return the indentation level (number of spaces on the beginning of the line)
pIndent = (+) <$> (indentOf <$> (pSpace <|> pTab)) <*> pIndent `opt` 0

-- returns tuple of (indentation level, result of parsing the second argument)
pIndentLine p = (,) <$> pIndent <*> p <* pEOL

-- SHOULD collect all lines below witch indentations greater or equal i
myParse p i = do
    (lind, expr) <- pIndentLine p
    if lind < i
        then pFail
        else do
            rest <- myParse p i `opt` []
            return $ expr:rest

-- sample inputs
s1 = " a\
   \\n a\
   \\n"

s2 = " a\
   \\na\
   \\n"

-- execution
pProgram = myParse (pSym 'a') 1 

parse p s = UU.parse ( (,) <$> p <*> pEnd) (createStr (LineColPos 0 0 0) s)

main :: IO ()
main = do 
    print $ parse pProgram s1
    print $ parse pProgram s2
    return ()

Which gives following output:

("aa",[])
Test.hs: no correcting alternative found

The result for s1 is correct. The result for s2 should consume first "a" and stop consuming. Where this error comes from?

4

1 に答える 1

1

構築中のパーサーは常に処理を続行しようとします。必要に応じて、入力が破棄または追加されます。ただしpFail行き止まりです。の単位要素として機能し<|>ます。

ただし、パーサーには、入力がパーサーによって認識される言語に準拠していない場合に備えて、他の代替手段はありません。あなたの仕様では、入力 s2 でパーサーを失敗させたいと言っています。今度は、それが失敗したというメッセージで失敗し、あなたは驚いています。

たぶん、失敗したくないが、それ以上の入力の受け入れを停止したいですか? その場合は に置き換えpFailますreturn []

テキストに注意してください:

do
    rest <- myParse p i `opt` []
    return $ expr:rest

で置き換えることができます(expr:) <$> (myParse p i `opt` [])

あなたの問題を解決する自然な方法は、おそらく次のようなものです

pIndented p = do i <- pGetIndent
             (:) <$> p <* pEOL  <*> pMany (pToken (take i (repeat ' ')) *> p <* pEOL)

pIndent = length <$> pMany (pSym ' ')
于 2013-08-15T08:13:40.947 に答える