我的数据类型
data S = Fac S | Mul S S | Nat Integer
deriving (Show)
和定义为
的语法S ::= S "!" | S "*" S | natural
到目前为止,我已经写完了
pa :: Parser S
pa = facst <|> multst <|> natst
facst = do
s <- pa
char '!'
return (Fac s)
multst = do
s1 <- pa
char '*'
s2 <- pa
return (Mul s1 s2)
natst = do
n <- natural
return (Nat n)
但是事实和多事不起作用。 natst仅适用于单个整数,例如“ 5”,而不适用于“ 56”。 我已经尝试过了
natst = do
n <- some natural
return (Nat n)
但是会产生一个错误。 有人可以指出我正确的方向吗?
答案 0 :(得分:2)
正如一些评论所建议的那样,您的语法是模棱两可的,并且包含左递归,因此像大多数解析器组合器库生成的递归下降解析器一样,这也是一个问题。参见例如How to remove ambiguity in the following grammar?和Left recursion elimination的问题解答。
就您而言,
S ::= S "!" | S "*" S | natural
具有左递归,因为"!"
和"*"
的产生都以S
开始。这是模棱两可的,因为目前尚不清楚S
中哪个应该首先在"*"
生成中派生:给定像1 * 2 * 3
这样的表达式,应该产生这个
* *
/ \ / \
1 * or * 3 ?
/ \ / \
2 3 1 2
1 * (2 * 3) (1 * 2) * 3
正如@melpomene指出的那样,它也是模棱两可的,因为1 * 2 !
可能产生
* !
/ \ |
1 ! or *
| / \
2 1 2
1 * (2 !) (1 * 2) !
一个既没有左递归也没有歧义的重写语法的例子(还有其他)是
S ::= natural S₁
S₁ ::= "!" | "*" S | ε
使用这种语法,1 * 2 * 3
将始终解析为</ p>
S
1 S₁
1 * S
1 * 2 S₁
1 * 2 * S
1 * 2 * 3 S₁
1 * 2 * 3 ε
表示*
变为右关联。并且1 * 2 !
将始终解析为</ p>
S
1 S₁
1 * S
1 * 2 S₁
1 * 2 !
意味着!
的优先级比*
高,我不知道是好是坏。
无论哪种方式,如果您希望解析器表达任意表达式,则可能要使用显式括号扩展语法,以便您可以覆盖每个运算符的默认优先级。
对于解析器本身,您可以直接根据重写的语法对其进行建模,例如:
parseS :: Parser S
parseS = do
n <- natural
f <- parseS1
return (f n)
natural :: Parser S
natural = do
n <- read <$> many1 digit
return (Nat n)
parseS1 :: Parser (S -> S)
parseS1 = parseFac <|> parseMul <|> parseNat
where
parseFac = do
char '!'
return (\s -> Fac s)
parseMul = do
char '*'
s2 <- parseS
return (\s1 -> Mul s1 s2)
parseNat = do
eof -- ε
return (\s -> s)
然后,您必须处理空格:
> parse parseS "" "1*2*3"
Right (Mul (Nat 1) (Mul (Nat 2) (Nat 3)))
> parse parseS "" "1 * 2 * 3"
Left (line 1, column 2):
unexpected ' '
expecting digit, "!", "*" or end of input
> parse parseS "" " 1*2*3"
Left (line 1, column 1):
unexpected " "
expecting digit
> parse parseS "" "1*2*3 "
Left (line 1, column 6):
unexpected ' '
expecting digit, "!", "*" or end of input
我会参考教程或书籍来正确完成这一部分。
最后,您可能想使用各种解析器组合器库的某些高级功能,例如chainr1
或Text.Megaparsec.Expr
's makeExprParser
,这些功能试图以一种不太麻烦的方式处理此类问题。不过,在使用它们之前,明智的做法是像您当前正在做的那样,通过手动进行解析器来了解它们是如何实现的。例如,如何转换上面的解析器,以使"*"
是左关联的,或者"!"
的优先级较低?