我想解析缩进或格式化为Parsec
的命令列表。例如,我的列表将采用以下格式:
Command1 arg1 arg2 Command1 arg1 arg2 Command1 arg1 arg2
Command2 arg1 Command3 arg1 arg2 arg3
Command3 arg1 arg2 arg3
Command4
Command3 arg1 arg2 arg3 Command2 arg1
Command4
Command4
Command5 arg1 Command2 arg1
这些命令应该在解析器中通过状态更改逐列进行解析。
我的想法是将命令收集到单独的字符串列表中,并将这些字符串解析到一个子解析器中(在主解析器内部执行)。
我检查了Parsec库的API,但没有找到执行此操作的函数。
我考虑使用runParser
,但是此函数仅提取解析器的结果,而不提取其状态。
我还考虑过制作一个受runParsecT
和mkPT
启发的函数来制作自己的解析器,但是构造函数ParsecT
或initialPos
不可用(库)
是否可以使用Parsec
在解析器中运行子解析器?
如果没有,megaparsec之类的图书馆可以解决我的问题吗?
答案 0 :(得分:2)
不是完整的答案,更多的是需要澄清的问题:
是否需要构建字符串列表? 我希望解析输入并将其转换为更特殊的数据类型。这样,您就可以使用haskell的类型保证了。
我将从为命令定义数据类型开始
data Command = Command1 Argtype1
| Command2 Argtype2
| Command3 Argtype1 Argtype2
data Argtype1 = Arg1 | Arg2 | ArgX
data Argtype2 = Arg2_1 | Arg2_2
之后,您可以解析输入并将其放入数据类型。
在解析结束时,您可以mappend
的结果(即使用操作符(:)在最前面添加的列表)。
您最终得到的数据类型为[Command]。 这样您就可以进一步工作。
要解析文本,您可以按照megaparsec软件包的介绍进行操作,网址为 (https://markkarpov.com/megaparsec/parsing-simple-imperative-language.html)
或者您是说完全不同的东西吗?也许每行(包含一些命令)全部是状态机的一个输入,并且状态机相对于命令发生了变化?然后我想知道为什么状态机将被实现为解析器。
答案 1 :(得分:1)
作为起点,“如何制作子解析器”的最简单答案是使用monadic绑定,适用的<*>
,替代项<|>
和库提供的组合器。假设每个命令属于一种类型(如Hans Kruger的回答),并且具有任意数量的列,则下面的示例可能是一个很好的模板。
import Text.Parsec
import Text.Parsec.Char
import Data.List(transpose)
cmdFileParser :: Parsec s u [[CommandType]]
cmdFileParser = sepBy sepParser cmdLineParser
where
sepParser = newline --From Text.Parsec.Char
cmdLineParser :: Parsec s u [CommandType]
cmdLineParser = sepBy sepParser cmdParser
where
sepParser = tab
cmdParser :: Parsec s u CommandType
cmdParser = parseCommand1
<|> parseCommand2
<|> parseCommand3
<|> etc
然后,在解析之后,将[[CommandType]]
转置为按列分组命令
main = do
...
let ret = runParser cmdFileParser
"debug string telling what was parsed"
stringToParse
case ret of
Left e -> putStrLn "wasn't parsed"
Right cmds -> doSomethingWith (transpose cmds)
我会说以上是一种典型的方法。当然有变化。例如,如果您知道应该只有三列,则可以使用下面的而不是上面的cmdLineParser
cmdLineParser :: Parsec s u (CommandType,CommandType,CommandType)
cmdLineParser = (\a b c -> (a,b,c)) <$> ct <*> ct <*> cmdParser
where
ct = cmdParser <* tab
我会说使用getState
是非典型的。当我第一次开始使用Parsec时,我记得工作后得到的东西就像我认为的一样,但这并不漂亮。当然,如果您只想返回字符串,则可以随时解析除换行符和制表符之外的任何字符。
cmdParser :: Parsec s u String
cmdParser = many (noneOf "\n\t")
但是,请谨慎使用以上内容。以前,我对使用many
感到很沮丧,因为这花了太多时间或总是成功。因此,我不太相信确切的格式会为您提供命令字符串。另外,如果只是将该命令解析为字符串,然后在main
中重新解析该命令,则将解析两次!