复杂的Parsec解析器
我不太清楚还有其他问题。我认为我需要这里的一般指导。我有这样的事情:复杂的Parsec解析器
expr = buildExpressionParser table term
<?> "expression"
term = choice [
(float >>= return . EDouble)
, try (natural >>= return . EInteger)
, try (stringLiteral >>= return . EString)
, try (reserved "true" >> return (EBool True))
, try (reserved "false" >> return (EBool False))
, try assign
, try ifelse
, try lambda
, try array
, try eseq
, parens expr
]
<?> "simple expression"
当我测试解析器,我主要是让问题...就像当我尝试解析
(a,b) -> "b"
它是由lambda
解析器接受,但expr
解析器讨厌它。有时它甚至完全停留在永恒的规则中。
我已阅读Write Yourself a Scheme,但它只解析Scheme的同质源。
也许我一般都在想错方向。
编辑:这里的内部解析器:
assign = do
i <- identifier
reservedOp "="
e <- expr
return $ EAssign i e
ifelse = do
reserved "if"
e <- expr
reserved "then"
a <- expr
reserved "else"
b <- expr
return $ EIfElse e a b
lambda = do
ls <- parens $ commaSep identifier
reservedOp "->"
e <- expr
return $ ELambda ls e
array = (squares $ commaSep expr) >>= return . EArray
eseq = do
a <- expr
semi <|> (newline >>= (\x -> return [x]))
b <- expr
return $ ESequence a b
table = [
[binary "*" EMult AssocLeft, binary "/" EDiv AssocLeft, binary "%" EMod AssocLeft ],
[binary "+" EPlus AssocLeft, binary "-" EMinus AssocLeft ],
[binary "~" EConcat AssocLeft],
[prefixF "not" ENot],
[binaryF "and" EAnd AssocLeft, binaryF "or" EAnd AssocLeft]
]
并以“讨厌”我的意思是,它告诉我,它期待一个整数或浮点数。
似乎有左递归,这将导致解析器挂起,如果在term
的choice
还未获得eseq
:
expr
- >term
- >eseq
- >expr
的term
(a,b)
会不解析为lambda
或array
,因此它将落入eseq
循环中。
我不明白为什么(a,b) -> "b"
不解析为expr
,因为在长期的choice
应该打在了lambda
,你说的作品,达到了eseq
之前。解析错误中报告的位置是什么?
我找了30分钟,看不到它。做得好。 +1 – 2011-05-04 17:37:47
感谢您的意见。我把'eseq','array'和'lambda'换成了一个不同的combinator,比如现在更多的ebnf。它现在似乎工作。 – Lanbo 2011-05-05 19:02:38
爱德华在评论和我都试图做的是精神上运行你的解析器,这是有点困难,没有更多的解析器继续。我要在这里做一些猜测,也许他们会帮助你改进你的问题。
猜测1):您已尝试GHCI> parse expr "(input)" "(a,b) -> \"b\"
,它已返回Left …
。知道错误是什么会有帮助。
猜测2):您也试过GHCI> parse lambda "(input)" "(a,b) -> \"b\"
,它返回Right …
。基于这个爱德华和我都推断,在你的term
解析器或可能在生成的expr
解析器中的某处存在冲突这是解析器的某些部分成功匹配字符串的开头并返回一个值,但是什么遗体不再有效。如果您想尝试GHCI> parse term "(input)" "(a,b) -> \"b\"
会很有帮助,因为这会让我们知道问题出在term
还是expr
。
猜测3):字符串"(a,b)"
本身就是一个语法中的有效表达式,因为它已经编程了。 (虽然可能不像你意图来编程它;-)。尝试通过expr解析器发送,看看会发生什么。
猜测4):您的文法是递归的。这是导致它卡住并永久循环的原因。 Parsec是一个LL(k)解析器。如果您习惯于LR(1)或LR(k)解析器的Yacc和family,则递归规则完全相反。如果你不明白这最后一句话,那就行,但请告诉我们。
猜测5):表达式构建器中的代码看起来像来自函数的文档。我想你也可能在某处找到了term
表达式。如果是这种情况,你指出它来自哪里。如果不是,你能用几句话解释你认为term
应该如何工作。
一般建议:经大量try
陈述最终(又名现在)打算给你造成的悲伤。它们在某些情况下很有用,但也有点顽皮。如果下一个角色能够确定成功的选择,那么他们就不需要了。如果你只是试图让一些东西运行,那么回溯会减少中间形式的数量,但是它也隐藏了病态情况并使错误更加模糊。
不错的心灵调试。至少我会把我的钱放在这些猜测中的一个上。 – 2011-05-04 00:06:23
术语'“(a,b)”'导致循环。而'term'应该是用来表达我想要允许的一切词语。我实际上想要像'a = z +(如果是true,然后是1 else 2)' – Lanbo 2011-05-04 07:07:24
我们需要内部语句的内部,因为你可能有语法冲突。另外,你是什么意思“恨它”? – 2011-05-03 22:05:56