问题描述
我正在尝试基于缩进的编程语言,并且试图解析类似的内容:
expr1 :
expr2
expr3
在这里,本质上:
表示新的缩进块的开始,因此expr1
完全不相关,其思想是:
可以出现在行中的任何位置,并且必须是该行的最后一个标记。
我得到的这段代码或多或少有效:
block :: Parser Value
block = dbg "block" $ do
void $ symbol ":"
void $ eol
space1
(L.indentBlock spaceConsumer indentedBlock)
where
indentedBlock = do
e <- expr
pure (L.IndentMany nothing (\exprs -> pure $ Block () (e : exprs)) expr)
但是问题在于,在该示例中,仅使用适当的缩进来解析块的第一个表达式,而其他表达式必须更缩进,像这样
expr1 :
expr2
expr3
expr4
expr5
解决方法
我无法提供针对megaparsec的特定建议,因为我不知道该特定的库,但是我可以通过编写一些缩进的敏感语言解析器来给我我的智慧:如果您以单独的步骤进行lex和解析,并且在字典分析中添加indent_begin
和indent_end
。
我最终在与expr1
相同的位置解析了:
显然indentBlock
从最后一个参数开始时传递的解析器的列开始计数,所以想法是从行的开头(相对于当前缩进级别)开始解析,最终像这个:
block :: Parser Value
block =
L.indentBlock spaceConsumer indentedBlock
where
indentedBlock = do
caller <- callerExpression
args <- parseApplicationArgs
pure (L.IndentSome Nothing (exprsToAppBlock caller args) parse)
exprsToAppBlock caller args exprs =
pure (Application () caller (args <> [Block () exprs]))
,
我通常添加以下组合器:
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