【问题标题】:Left recursive arithmetic when creating parser with Haskell使用 Haskell 创建解析器时的左递归算法
【发布时间】:2022-12-16 21:39:42
【问题描述】:

我正在尝试使用 readP library 在 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),我真的只是从错误的角度来解决问题。但我不这么认为。

【问题讨论】:

  • 看来您需要重构语法。您应该将 a+b+c 视为以 a 开头,然后以 +b+c 开头的内容。因此,表达式是一个“术语”,后跟零个或多个“+术语”。术语是文字或带括号的表达式。要添加乘法(具有更高的优先级),您需要向“term”添加更多的产品。除非所有此类情况都具有相同的优先级,否则您不能拥有通用的“op”情况。我认为这里的关键术语是“LL(1) 语法”,您可以尝试搜索它以查看一些语法示例。
  • @chi 那么look 的思路和用法是什么?

标签: parsing haskell recursion functional-programming monads


【解决方案1】:

由于上述评论,我现在能够根据语法的变化提出一个解决方案。

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))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2022-08-09
    相关资源
    最近更新 更多