我将编写一个verilog(或vhdl)语言的解析器,并将对解析数据进行大量操作(某种转换).我打算解析真正的大文件(完整的Verilog设计,大到10K行),我最终会支持大部分的Verilog.我不介意输入,但每当我添加对其他规则的支持时,我不想重写代码的任何部分.
在Haskell,你会推荐哪个图书馆?我知道Haskell并且之前使用过Happy(玩).我觉得有可能使用Parsec来转换代码中的解析字符串(这是一个很好的加分).我没有使用uu-paringlib的经验.
那么要解析verilog/VHDL的全语法,推荐其中一个?我主要担心的是我可以随心所欲地操纵解析数据的简易性和"正确性".速度不是主要问题.
题
我知道Parsec
,uu-parsinglib
而且我在两者中都编写过解析器.最近我发现,存在一个问题uu-parsinglib
,可能会对其性能产生重大影响,我看不到解决问题的方法.
让我们考虑遵循Parsec解析器:
pa = char 'a'
pb = char 'b'
pTest = many $ try(pa <* pb)
Run Code Online (Sandbox Code Playgroud)
相当于uu-parsinglib
什么?它不会是以下内容:
pa = pSym 'a'
pb = pSym 'b'
pTest = pList_ng (pa <* pb)
Run Code Online (Sandbox Code Playgroud)
所不同的是,在Parsec
,many
会吃(pa <* pb)
(对"ab"
)贪婪,直到它不再匹配,而在uu-parsinglib
,pList_ng
不贪心,所以每次分析后,将保留在内存中可能原路返回的方式(pa <* pb)
.
有没有办法写出类似pList(try(pa <* pb))
的内容uu-parsinglib
?
例
一个很好的例子
pExample = pTest <* (pa <* pb)
Run Code Online (Sandbox Code Playgroud)
和一个样本输入"ababab" …
我想创建一个解析器组合器,它将收集当前位置以下的所有行,这些缩进级别将大于或等于某些i
.我认为这个想法很简单:
消耗一条线 - 如果它的缩进是:
让我们考虑以下代码:
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 …
Run Code Online (Sandbox Code Playgroud) 问题
我今天遇到了一个问题,我不知道如何解决它.这对我来说很奇怪,因为我写的代码应该(根据我目前的知识)是正确的.
所以下面你可以找到一个示例解析器组合器.最重要的是pOperator
,它以非常简单的方式(仅用于演示目的)构建运算符AST.它消耗"x"并且可以消耗由空格分隔的多个"x".
我也有pParens
combinator,定义如下:
pPacked pParenL (pWSpaces *> pParenR)
Run Code Online (Sandbox Code Playgroud)
所以它在关闭括号之前消耗空格.
样本输入/输出
正确的输入/输出应该是:
in: "(x)"
out: Single "x"
in: "(x )"
out: Single "x"
Run Code Online (Sandbox Code Playgroud)
但我得到了:
in: "(x)"
out: Single "x"
in: "(x )"
out: Multi (Single "x") (Single "x")
-- Correcting steps:
-- Inserted 'x' at position LineColPos 0 3 3 expecting one of ['\t', ' ', 'x']
Run Code Online (Sandbox Code Playgroud)
但是在第二个例子中我得到了错误 - 解析器的行为就像它贪婪地吃了一些令牌(而且没有贪婪的操作).
我会感谢任何帮助.
示例代码
import Prelude hiding(lex)
import Data.Char hiding (Space)
import qualified Text.ParserCombinators.UU as UU
import Text.ParserCombinators.UU …
Run Code Online (Sandbox Code Playgroud) 作为真实语言解析器的简化子问题,我试图为虚构语言的表达式实现一个解析器,它看起来类似于标准命令式语言(如Python,JavaScript等).其语法具有以下构造:
[a-zA-Z]+
)+
和*
和括号的算术表达式.
(例如foo.bar.buz
)(1, foo, bar.buz)
)(删除歧义一元组写成(x,)
)foo(1, bar, buz())
)foo()()
,因为foo()
可能返回函数是合法的)所以这个语言中的一个相当复杂的程序是
(1+2*3, f(4,5,6)(bar) + qux.quux()().quuux)
Run Code Online (Sandbox Code Playgroud)
相关性应该是
( (1+(2*3)), ( ((f(4,5,6))(bar)) + ((((qux.quux)())()).quuux) ) )
Run Code Online (Sandbox Code Playgroud)
我目前正在使用非常好的uu-parsinglib
应用解析器组合器库.
第一个问题显然是直观的表达式语法(expr -> identifier | number | expr * expr | expr + expr | (expr)
左递归.但我可以使用pChainl
组合器解决这个问题(参见parseExpr
下面的例子).
剩下的问题(因此这个问题)是函数应用程序,其函数返回其他函数(f()()
).同样,语法是递归的expr -> fun-call | ...; fun-call -> …
让我们看看代码片段:
pSegmentBegin p i = pIndentExact i *> ((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure []))
Run Code Online (Sandbox Code Playgroud)
如果我在我的解析器中将此代码更改为:
pSegmentBegin p i = do
pIndentExact i
((:) <$> p i <*> ((pEOL *> pSegment p i) <|> pure []))
Run Code Online (Sandbox Code Playgroud)
我有一个错误:
canot compute minmal length of a parser due to occurrence of a moadic bind, use addLength to override
Run Code Online (Sandbox Code Playgroud)
我认为上面的解析器应该以相同的方式运行.为什么会出现此错误?
编辑
上面的例子很简单(为了简化问题),如下所述,这里没有必要使用do notation,但我希望它使用的实际情况如下:
pSegmentBegin p i = do
j <- pIndentAtLast i
(:) <$> p j <*> ((pEOL …
Run Code Online (Sandbox Code Playgroud)