我通常添加以下组合子:
import qualified Text.Megaparsec.Char.Lexer as L
indented :: Pos -> Parser a -> Parser (Pos, a)
indented ref p = do pos <- L.indentGuard space GT ref
v <- p
pure (pos, v)
aligned :: Pos -> Parser a -> Parser a
aligned ref p = L.indentGuard space EQ ref *> p
然后您可以使用L.indentLevel
来获取参考缩进。
以下是解析包含错误处理的语句块的示例:
blocked1 :: Pos -> Parser a -> Parser [a]
blocked1 ref p = do (pos, a) <- indented ref p
rest <- many (try $ helper pos)
fpos <- getPosition
rest' <- traverse (reportErrors pos) rest
setPosition fpos
pure (a : rest')
where helper pos' = do pos <- getPosition
a <- p
when (sourceColumn pos <= ref) $ L.incorrectIndent EQ pos' (sourceColumn pos)
pure (pos, a)
reportErrors ref (pos, v) = setPosition pos *>
if ref /= sourceColumn pos
then L.incorrectIndent EQ ref (sourceColumn pos)
else pure v
blocked :: Pos -> Parser a -> Parser [a]
blocked ref p = blocked1 ref p <|> pure []
block :: Pos -> Parser (Block ParserAst)
block ref = do
s <- blocked1 ref stmt
pure $ Block s
funcDef :: Parser (FuncDef ParserAst)
funcDef = annotate $
do pos <- L.indentLevel
symbol "def"
h <- header
l <- localDefs
b <- block pos
pure $ FuncDef h l b