4

我一直在尝试使用 buildExpressionParser 来解析一种语言,而且我几乎拥有它。感谢Parsec.Expr 重复的前缀/后缀运算符不支持解决我的一个大问题。

这个代码片段说明了(我希望是什么)我的最后一个困难:

import Text.Parsec.Expr
import Text.Parsec

data Expr = Lit Char | A1 Expr | A2 Expr | B Expr Expr
  deriving (Show)

expr :: Parsec String () Expr
expr = buildExpressionParser table (fmap Lit digit)

prefix p = Prefix . chainl1 p $ return (.)

table =
  [ [prefix $ char ',' >> return A1]
  , [Infix   (char '*' >> return B) AssocNone]
  , [prefix $ char '.' >> return A2]]

这成功(并且正确)解析,,0, ..0, .,0, .0*0, 和,0*0; 但是,它不能解析,.0or .0*.0。我可以看到为什么这两个不解析,但是我看不到如何更改解析器,以便成功都不会改变并且两个失败都会解析。

“解决”此问题的一种方法是更改(fmap Lit digit)​​为(fmap Lit Digit <|> expr),但随后解析器将循环而不是出错。

欢迎咨询。

编辑:以下解析是关键:

> parseTest expr ".0*0"
A2 (B (Lit '0') (Lit '0'))

> parseTest expr ",0*0"
B (A1 (Lit '0')) (Lit '0')
4

1 回答 1

5

要得到 '。' 和 ',' 在某种程度上你可以把它们放在一起:

import Text.Parsec.Expr
import Text.Parsec

data Expr = Lit Char | A1 Expr | A2 Expr | B Expr Expr
  deriving (Show)

expr :: Parsec String () Expr
expr = buildExpressionParser table  (fmap Lit digit)
prefix p = Prefix . chainl1 p $ return (.)

table =
  [  [prefix $ (char ',' >> return A1) <|> (char '.' >> return A2)]
  , [Infix   (char '*' >> return B) AssocNone]
  , [prefix $ (char ',' >> return A1)] 
  ]

-- *Main> let f = parseTest expr
-- *Main> f ".,0"
-- A2 (A1 (Lit '0'))
-- *Main> f ".0*.0"
-- B (A2 (Lit '0')) (A2 (Lit '0'))
-- *Main> f ".0*,.0"
-- B (A2 (Lit '0')) (A1 (A2 (Lit '0')))
-- *Main> f ".,.0"
-- A2 (A1 (A2 (Lit '0')))
-- *Main> f ",.0"
-- A1 (A2 (Lit '0'))

编辑,这是较早的明显不充分的尝试

 table =
   [  [prefix $ (char ',' >> return A1) <|> (char '.' >> return A2)]
   , [Infix   (char '*' >> return B) AssocNone]
   ]
于 2012-06-24T04:36:54.577 回答