[英]Left recursive arithmetic when creating parser with Haskell
我正在嘗試使用在其(算術)輸出中保持關聯的readP
庫在 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),我真的只是從錯誤的角度來解決問題。 但我不這么認為。
考慮chainl1:: ReadP a -> ReadP (a -> a -> a) -> ReadP a
。 大多數解析器組合器庫都有chainl
和chainr
函數族,用於以左關聯或右關聯方式解析運算符。 你可以像這樣應用它:
pExpr = chainl1 numConst (Oper Plus <$ symbol "+")
chainl1
的實現正是 chi 在評論中推薦的以及您的答案實現的:它解析p
,然后解析任意數量的op
+ p
對。
這解決了您當前的問題,但是一旦您添加了第二個運算符,您就會對優先級做出一些決定。 如果您希望+
和*
具有相同的優先級(不尋常),您可以只使用這種方法但豐富分隔符解析器。 如果您希望*
具有更高的優先級,則需要將pExpr
分解為多個解析器:而不是Expr
,解析Product
的Sum
,諸如此類。 然后你需要一個chainl1
來解析Sum
的條款,其中每個條款本身都用chainl1
解析以產生Product
。
由於上述評論,我現在能夠根據語法的變化提出一個解決方案。
parseString input = readP_to_S (do
e <- pExpr
token eof
return e) input
pExpr :: Parser Exp
pExpr =
(do
pv <- numConst
pv2 <- pOpRight pv
return pv2
)
<|>
(do
pv <- numConst
skipSpaces
return pv)
numConst :: Parser Exp
numConst =
(do
skipSpaces
y <- munch isDigit
return (Const (IntVal (read y)))
)
pOpRight :: Exp -> Parser Exp
pOpRight e1 = (do
op <- symbol "+"
skipSpaces
e2 <- pExpr
pv <- pOper op e1 e2 --
return pv)
pOper :: String -> Exp -> Exp -> Parser Exp
pOper "+" exp1 exp2 = (do return (Oper Plus exp1 exp2))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.