我想要创建一个解析器组合器,它将收集当前位置以下的所有行,缩进级别将大于或等于某个i。我认为这个想法很简单:
使用一行-如果其缩进是:
让我们考虑以下代码:
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 ()它提供了以下输出:
("aa",[])
Test.hs: no correcting alternative founds1的结果是正确的。s2的结果应该首先消费"a“,然后停止消费。这个错误从何而来?
发布于 2013-08-15 08:13:40
您正在构建的解析器将始终尝试继续;如果有必要,输入将被丢弃或添加。然而,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 ' ')https://stackoverflow.com/questions/18237578
复制相似问题