【发布时间】:2022-12-16 21:39:42
【问题描述】:
我正在尝试使用 readP library 在 Haskell 中创建一个解析器,它在它的(算术)输出中保持关联。在下面的简化代码中,如果在表达式的左侧部分调用pOp,我显然会得到一个无限循环(请参阅注释掉的代码),或者我会得到一个正确的关联输出,例如2+(4+(6+8)),相当于:
ghci> parseString "2+4+6+8"
[(Oper Plus (Const (IntVal 2)) (Oper Plus (Const (IntVal 4)) (Oper Plus (Const (IntVal 6)) (Const (IntVal 8)))),"")]
MVE:
import Data.Char
import Text.ParserCombinators.ReadP
--import Text.Parser.Char
import Control.Applicative ((<|>))
type Parser a = ReadP a
data Value =
IntVal Int
deriving (Eq, Show, Read)
data Exp =
Const Value
| Oper Op Exp Exp
deriving (Eq, Show, Read)
data Op = Plus
deriving (Eq, Show, Read)
space :: Parser Char
space = satisfy isSpace
spaces :: Parser String
spaces = many space
space1 :: Parser String
space1 = many1 space
symbol :: String -> Parser String
symbol = token . string
token :: Parser a -> Parser a
token combinator = (do spaces
combinator)
parseString input = readP_to_S (do
e <- pExpr
token eof
return e) input
pExpr :: Parser Exp
pExpr =
(do
pv <- pOp
return pv)
<|>
(do
pv <- numConst
skipSpaces
return pv)
numConst :: Parser Exp
numConst =
(do
skipSpaces
y <- munch isDigit
return (Const (IntVal (read y)))
)
pOp :: Parser Exp
pOp = (do
e1 <- numConst -- pExpr
skipSpaces
op <- symbol "+"
e2 <- pExpr
pv <- pOper op e1 e2 --
return pv)
pOper :: String -> Exp -> Exp -> Parser Exp
pOper "+" exp1 exp2 = (do return (Oper Plus exp1 exp2))
我尝试了不同的策略,例如使用上述文档中的 look 来展望未来,然后使用 "("++ e ++ ")" 获取返回的字符串并在其周围应用括号,其中 e 是表达式,然后有一个单独的函数处理对括号表达式的调用以避免循环。但这不是一个可行的解决方案,因为您不能像对原始输入(look)使用 readP 库函数那样对 look 的结果值使用它。
任何想法如何解决这个问题。我不知道我是否一开始就错误地表述了语法 (BNF),我真的只是从错误的角度来解决问题。但我不这么认为。
【问题讨论】:
-
看来您需要重构语法。您应该将
a+b+c视为以a开头,然后以+b和+c开头的内容。因此,表达式是一个“术语”,后跟零个或多个“+术语”。术语是文字或带括号的表达式。要添加乘法(具有更高的优先级),您需要向“term”添加更多的产品。除非所有此类情况都具有相同的优先级,否则您不能拥有通用的“op”情况。我认为这里的关键术语是“LL(1) 语法”,您可以尝试搜索它以查看一些语法示例。 -
@chi 那么
look的思路和用法是什么?
标签: parsing haskell recursion functional-programming monads