uu-parsinglib中的计划外贪婪行为

uu-parsinglib中的计划外贪婪行为,parsing,haskell,parsec,parser-combinators,uu-parsinglib,Parsing,Haskell,Parsec,Parser Combinators,Uu Parsinglib,问题 我今天遇到了一个问题,我不知道如何解决它。这对我来说很奇怪,因为我写的代码应该(根据我目前的知识)是正确的 下面您可以找到一个解析器组合器示例。最重要的是pOperator,它以非常简单的方式(仅用于演示)构建一个操作符AST。 它使用“x”,并且可以使用多个由空格分隔的“x” 我还有pParenscombinator,它的定义如下: pPacked pParenL (pWSpaces *> pParenR) 因此,它在结束括号之前使用空格 样本输入/输出 正确的输入/输出应为:

问题

我今天遇到了一个问题,我不知道如何解决它。这对我来说很奇怪,因为我写的代码应该(根据我目前的知识)是正确的

下面您可以找到一个解析器组合器示例。最重要的是
pOperator
,它以非常简单的方式(仅用于演示)构建一个操作符AST。 它使用“x”,并且可以使用多个由空格分隔的“x”

我还有
pParens
combinator,它的定义如下:

pPacked pParenL (pWSpaces *> pParenR)
因此,它在结束括号之前使用空格

样本输入/输出

正确的输入/输出应为:

in: "(x)"
out: Single "x"

in: "(x )"
out: Single "x"
但我得到了:

in: "(x)"
out: Single "x"

in: "(x )" 
out: Multi (Single "x") (Single "x")
--  Correcting steps: 
--    Inserted  'x' at position LineColPos 0 3 3 expecting one of ['\t', ' ', 'x']
但是在第二个例子中,我得到了一个错误——解析器的行为就像贪婪地吃了一些令牌(并且没有贪婪操作)

如果有人帮我,我将不胜感激

示例代码

import Prelude hiding(lex)
import Data.Char hiding (Space)
import qualified Text.ParserCombinators.UU as UU
import           Text.ParserCombinators.UU hiding(parse)
import qualified Text.ParserCombinators.UU.Utils as Utils
import           Text.ParserCombinators.UU.BasicInstances hiding (Parser)


data El = Multi El El
        | Single String
        deriving (Show)


---------- Example core grammar ----------

pElement     = Single <$> pSyms "x"
pOperator    = applyAll <$> pElement <*> pMany (flip <$> (Multi <$ pWSpaces1) <*> pElement)

---------- Basic combinators ----------

applyAll x (f:fs) = applyAll (f x) fs
applyAll x []     = x

pSpace    = pSym ' '
pTab      = pSym '\t'
pWSpace   = pSpace <|> pTab
pWSpaces  = pMany pWSpace
pWSpaces1 = pMany1 pWSpace
pMany1 p  = (:) <$> p <*> pMany p

pSyms []       = pReturn []
pSyms (x : xs) = (:) <$> pSym x <*> pSyms xs

pParenL     = Utils.lexeme $ pSym '('
pParenR     = Utils.lexeme $ pSym ')'
pParens     = pPacked pParenL (pWSpaces *> pParenR)

---------- Program ----------

pProgram = pParens pOperator
-- if you replace it with following line, it works:
--  pProgram = pParens pElement
-- so it seems like something in pOperator is greedy

tests = [ ("test", "(x)")
        , ("test", "(x )")
        ]

---------- Helpers ----------

type Parser a = P (Str Char String LineColPos) a

parse p s = UU.parse ( (,) <$> p <*> pEnd) (createStr (LineColPos 0 0 0) s)

main :: IO ()
main = do 
    mapM_ (\(desc, p) -> putStrLn ("\n=== " ++ desc ++ " ===") >> run pProgram p) tests
    return ()

run :: Show t =>  Parser t -> String -> IO ()
run p inp = do  let (a, errors) =  parse p inp
                putStrLn ("--  Result: \n" ++ show a)
                if null errors then  return ()
                               else  do putStr ("--  Correcting steps: \n")
                                        show_errors errors
                putStrLn "-- "
             where show_errors :: (Show a) => [a] -> IO ()
                   show_errors = sequence_ . (map (putStrLn . show))
导入前奏隐藏(lex)
导入数据。字符隐藏(空格)
将限定的Text.ParserCombinators.UU导入为UU
导入Text.ParserCombinators.UU隐藏(解析)
将限定的Text.ParserCombinators.UU.Utils导入为Utils
导入Text.ParserCombinators.UU.BasicInstances隐藏(解析器)
数据El=多El
|单串
派生(显示)
----------示例核心语法----------
pElement=单个心音“x”
pOperator=applyAll PELENT pMany(翻转(多页)
----------节目----------
p程序=pParens操作员
--如果将其替换为以下行,则它可以工作:
--p程序=pParens pElement
--看来pOperator里有些东西很贪婪
测试=[(“测试”,“x”)
(“测试”,“x”)
]
----------助手----------
类型解析器a=P(Str Char String LineColPos)a
parse p s=UU.parse((,)p pEnd)(createStr(LineColPos 0)s)
main::IO()
main=do
mapM(\(desc,p)->putStrLn(“\n==”++desc++“==”>>运行程序p)测试
返回()
运行::Show t=>Parser t->String->IO()
运行p inp=do let(a,errors)=解析p inp
putStrLn(“--Result:\n”++显示a)
如果为null错误,则返回()
否则执行PUTSR(“--更正步骤:\n”)
显示错误
putStrLn“
其中show_errors::(show a)=>[a]->IO()
show_errors=序列(映射(putStrLn.show))
重要

pOperator    = applyAll <$> pElement <*> pMany (flip <$> (Multi <$ pWSpaces1) <*> pElement)

pOperator=applyAll-pElement-pMany(翻转)(多pMany的定义如下:

pMany :: IsParser p => p a -> p [a]
pMany p = pList p
这就提出了解决方案。当看到空间时,我们不应立即承诺继续使用更多x-E,因此我们定义:

pMany :: IsParser p => p a -> p [a]
pMany_ng p = pList_ng p
当然,你也可以立即打电话给pList_ng。最好是写:

pParens (pChainr_ng (pMulti <$ pWSpaces1) px) -- 

我没有一个好的解决方案,但你的描述似乎正是发生的事情。如果我定义
let pOperator=applyAll pElement(pMany(flip(Multi@JohnL:这很奇怪。请注意,将
pProgram=pParens pOperator
替换为
pProgram=pParens pElement
会得到很好的结果(这当然也不能解决问题),但它表明,
pMany
可以按预期工作-不能消耗任何元素。@JohnL:另外,这个问题不能用
…pure[]
解决,因为它只对1个字符输入有效,例如,
(x x)
。好的,这个问题“理论上可以解决”通过将库
pMany
combinator替换为自定义库,并替换大量库函数(如
pChainl
等,以使用我们的自定义
pMany
combinator)。这当然是一个丑陋的解决方案,但到目前为止它仍然有效。我希望看到合适的解决方案。自定义的
pMany
可以声明如下:
pMany p=(:)p pMany p pure[]
再次是一个解决方案,但pMany是否可以用pList\ng替换?
pParens (pChainr_ng (pMulti <$ pWSpaces1) px) --