简体   繁体   English

解析 Haskell 中的左结合仅用于某些操作

[英]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 < 33 < 4都不是ArithTerm ,因此不能出现在<的另一侧。 1 < 2 + 2 is fine because 1 and 2 + 2 are both ArithTerm s. 1 < 2 + 2很好,因为12 + 2都是ArithTerm

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM