Proper line layout in uu-parsinglib in Haskell

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

Use a line - if indented:

  • ok -> do this for the following lines
  • Wrong → Failure

Consider the 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 pi = do (lind, expr) <- pIndentLine p if lind < i then pFail else do rest <- myParse pi `opt` [] return $ expr:rest -- sample inputs s1 = " a\ \\na\ \\n" s2 = " a\ \\na\ \\n" -- execution pProgram = myParse (pSym 'a') 1 parse ps = 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 the following conclusion:

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

The result for s1 correct. The result for s2 to consume "a" first and stop consuming. Where does this error come from?

+6
source share
1 answer

The parsers you build will always try to continue; if necessary, the entry will be discarded or added. However, pFail is a dead end. It acts as a unit element for <|> .

In your parser, however, there is no other alternative if the input does not match the language recognized by the parser. In your specification, you say you want the parser to fail on s2 input. Now he fails with the message that this fails, and you are surprised.

Maybe you do not want it to fail, but you want to stop accepting further data? In this case, replace pFail with return [] .

Please note that the text:

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

can be replaced by (expr:) <$> (myParse pi `opt` [])

The natural way to solve your problem is probably something like

 pIndented p = do i <- pGetIndent (:) <$> p <* pEOL <*> pMany (pToken (take i (repeat ' ')) *> p <* pEOL) pIndent = length <$> pMany (pSym ' ') 
+1
source

Source: https://habr.com/ru/post/951746/


All Articles