[英]Parsing left associative in Haskell for only some operations
I am trying to make a parser that will be able to parse expressions such as 2<3
succesfully as Oper Less (Const (IntVal 2)) (Const (IntVal 3))
while at the same time failing parseing chains such as 2 < 3 < 4
while also at the same time still being able to parse succesfully 2+2 < 5
.我正在尝试制作一个解析器,它能够像Oper Less (Const (IntVal 2)) (Const (IntVal 3))
一样成功解析2<3
等表达式,同时无法解析 2 < 2 < 3 < 4
等链2 < 3 < 4
同时仍然能够成功解析2+2 < 5
。 I have tried to use chainl1
to keep my operations left associative for both +
, -
and other operations.我尝试使用chainl1
使我的操作与+
、 -
和其他操作保持关联。 My problem seems to be with pOperHelper
and when using pTerm
in it.我的问题似乎出在pOperHelper
以及在其中使用pTerm
时。 It might be because I don't grasp chainl1
fully.可能是因为我没有完全掌握chainl1
。 I get the following output我得到以下 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
for the MVE below:对于下面的 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"
Why is the Prelude.read
occuring though, and is there a smarter way I can make use of chainl1
or similar and accomplish what I intend?为什么会出现Prelude.read
,有没有更聪明的方法可以使用chainl1
或类似的方法来完成我想要的?
chain*
parsers are specifically designed to allow things like 2 < 3 < 4
. chain*
解析器专门设计用于允许2 < 3 < 4
之类的东西。 What you want is a grammar that distinguishes between arithmetic operators and comparison operators.您需要的是一种区分算术运算符和比较运算符的语法。 For example, you are currently using a grammar like例如,您当前使用的语法如下
Expr -> Term (Op Term)+
Term -> Digit +
Op -> '+' | '-' | '<' | '>'
Digit -> '0' | ... | '9'
when what you want is something more complicated, which creates comparison operators differently (and with lower precedence) than arithmetic operators.当你想要的是更复杂的东西时,它创建的比较运算符与算术运算符不同(并且优先级较低)。 Something like就像是
Expr -> ArithTerm (CompOp ArithTerm)+
CompOp -> '<' | '>'
ArithTerm -> ArithTerm (ArithOp NumTerm) | NumTerm
NumTerm -> Digit +
ArithOp -> '+' | '-'
Digit -> '0' | ... | '9'
Now 2 < 3 < 4
is not allowed, because neither 2 < 3
nor 3 < 4
is an ArithTerm
, and so can't appear on one side other of <
.现在2 < 3 < 4
是不允许的,因为2 < 3
和3 < 4
都不是ArithTerm
,因此不能出现在<
的另一侧。 1 < 2 + 2
is fine because 1
and 2 + 2
are both ArithTerm
s. 1 < 2 + 2
很好,因为1
和2 + 2
都是ArithTerm
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.