簡體   English   中英

解析 Haskell 中的左結合僅用於某些操作

[英]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 < 33 < 4都不是ArithTerm ,因此不能出現在<的另一側。 1 < 2 + 2很好,因為12 + 2都是ArithTerm

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM