如何使用最新版本Parsec.Indent库编写缩进敏感语法的解析器
问题原因
你遇到的类型错误是由于新版indents库的实现逻辑变更导致的:旧版示例中手动基于State SourcePos封装缩进解析栈的写法已经不再适用,新版库已经通过IndentT monad转换器封装了所有缩进状态管理逻辑,库提供的indented等函数要求解析器的monad栈必须包含IndentT,你自定义的IParser类型不满足这个要求,因此出现类型不匹配报错。
完整实现方案
下面给出解析你所需缩进格式整数列表的完整可运行代码:
import Text.Parsec import Text.Parsec.Indent import Text.Parsec.String (Parser) -- 正确的解析器类型定义,用IndentT封装Parsec解析器 type IParser a = IndentT Parser a -- 解析整数 parseInt :: IParser Integer parseInt = read <$> many1 digit <* spaces -- 解析缩进层级的整数 parseIndentedInt :: IParser Integer parseIndentedInt = indented *> parseInt -- 解析整个列表结构:先读列表名,再读下面所有同缩进层级的整数条目 parseList :: IParser (String, [Integer]) parseList = do listName <- many1 letter <* spaces items <- block parseIndentedInt return (listName, items) -- 解析入口函数 main :: IO () main = do let testContent = unlines [ "mylist", " 123", " 456" ] -- 运行解析:先跑IndentT,再调用Parsec的标准parse case parse (runIndentT parseList) "测试输入" testContent of Left err -> print err Right (name, items) -> putStrLn $ "列表名:" ++ name ++ ",元素:" ++ show items
关键说明
- 你不需要自己手动管理
SourcePos状态,IndentT已经内部封装了所有缩进计算、层级校验逻辑 block函数可以自动解析当前位置下缩进层级更深的所有同级别条目,不需要手动控制缩进判断- 运行解析时需要先调用
runIndentT把缩进转换器层展开,再传给Parsec的标准parse函数即可
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

