[英]Parsing left associative in Haskell for only some operations
我正在嘗試制作一個解析器,它能夠像Oper Less (Const (IntVal 2)) (Const (IntVal 3))
一樣成功解析2<3
等表達式,同時無法解析 2 < 2 < 3 < 4
等鏈2 < 3 < 4
同時仍然能夠成功解析2+2 < 5
。 我嘗試使用chainl1
使我的操作與+
、 -
和其他操作保持關聯。 我的問題似乎出在pOperHelper
以及在其中使用pTerm
時。 可能是因為我沒有完全掌握chainl1
。 我得到以下 output
ghci> parseString "2 < 3 < 4"
Left "Unexpected error"
ghci> parseString "2 < 3"
Right (Oper Less (Const (IntVal 2)) (Const (IntVal *** Exception: Prelude.read: no parse
對於下面的 MVE:
module MVEParser (ParseError, parseString, pOper, pOperHelper, pTerm, pExpr) where
import Data.Char
import Text.ParserCombinators.ReadP
import Control.Applicative ((<|>))
type ParseError = String -- you may replace this
type Parser a = ReadP a
data Value =
IntVal Int
deriving (Eq, Show, Read)
data Op = Plus | Minus | Less | Greater
deriving (Eq, Show, Read)
data Exp =
Const Value
| Oper Op Exp Exp
deriving (Eq, Show, Read)
space :: Parser Char
space = satisfy isSpace
spaceBeforeAfter :: String -> Parser String
spaceBeforeAfter x = do spaces; str <- string x; space; return str
spaces :: Parser String
spaces = many space
symbol :: String -> Parser String
symbol = token . string
token :: Parser a -> Parser a
token combinator = (do spaces
combinator)
pExpr :: Parser Exp
pExpr = {- chainl1 pTerm pOper +++ -}chainl1 pTerm (pOperHelper pOper)
pTerm :: Parser Exp
pTerm =
(do
skipSpaces
pv <- munch isDigit
skipSpaces
return (Const (IntVal (read pv))))
pOper :: ReadP (Exp -> Exp -> Exp)
pOper = (symbol "+" >> return (Oper Plus))
<|> (symbol "-" >> return (Oper Minus))
<|> (symbol "<" >> return (Oper Less))
<|> (symbol ">" >> return (Oper Greater))
pOperHelper :: ReadP (Exp -> Exp -> Exp) -> ReadP (Exp -> Exp -> Exp)
pOperHelper op = do
operator <- op
term <- pTerm
skipSpaces
nextChar <- look
case nextChar of
(c:_) | c `elem` ['<', '>'] -> pfail
_ -> return operator
parseString input = let x = readP_to_S (do e <- pExpr; token eof; return e) input
in case x of
[(a, "")] -> Right a
_ -> Left "Unexpected error"
為什么會出現Prelude.read
,有沒有更聰明的方法可以使用chainl1
或類似的方法來完成我想要的?
chain*
解析器專門設計用於允許2 < 3 < 4
之類的東西。 您需要的是一種區分算術運算符和比較運算符的語法。 例如,您當前使用的語法如下
Expr -> Term (Op Term)+
Term -> Digit +
Op -> '+' | '-' | '<' | '>'
Digit -> '0' | ... | '9'
當你想要的是更復雜的東西時,它創建的比較運算符與算術運算符不同(並且優先級較低)。 就像是
Expr -> ArithTerm (CompOp ArithTerm)+
CompOp -> '<' | '>'
ArithTerm -> ArithTerm (ArithOp NumTerm) | NumTerm
NumTerm -> Digit +
ArithOp -> '+' | '-'
Digit -> '0' | ... | '9'
現在2 < 3 < 4
是不允許的,因為2 < 3
和3 < 4
都不是ArithTerm
,因此不能出現在<
的另一側。 1 < 2 + 2
很好,因為1
和2 + 2
都是ArithTerm
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.