我不知道怎么回答.我想我需要一般指导.我有这样的事情:
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"
Run Code Online (Sandbox Code Playgroud)
但是,当我测试该解析器时,我主要遇到问题...就像我尝试解析时一样
(a,b) -> "b"
Run Code Online (Sandbox Code Playgroud)
它被lambda解析器接受,但expr解析器讨厌它.有时它甚至完全挂在永恒的规则中.
我已经阅读了自己写一个方案,但它只解析了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]
]
Run Code Online (Sandbox Code Playgroud)
并且"讨厌它"我的意思是它告诉我它需要一个整数或一个浮点.
爱德华在评论中和我都想做的是精神上运行你的解析器,如果没有更多的解析器继续下去,这有点困难.我将在这里做一些猜测,也许他们会帮助你改进你的问题.
猜猜1):你已经尝试过GHCI> parse expr "(input)" "(a,b) -> \"b\"它已经回来了Left ….了解错误是有帮助的.
猜猜2):你也试过了GHCI> parse lambda "(input)" "(a,b) -> \"b\",然后又回来了Right ….基于这个Edward,我已经推断出你的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陈述最终(也就是现在)会让你感到悲伤.它们在某些情况下很有用,但也有点顽皮.如果下一个字符可以确定哪个选项应该成功,则不需要它们.如果你只是想让一些东西运行很多回溯会减少中间形式的数量,但它也会隐藏病态情况并使错误更加模糊.