Haskell:运算符分析器一直在处理未定义的而不是输入



我正在练习编写解析器。我使用Tsodings JSON Parser视频作为参考。我试图通过解析任意长度的算术来添加它,我已经想出了下面的AST。

data HVal
= HInteger Integer -- No Support For Floats
| HBool Bool
| HNull
| HString String
| HChar Char
| HList [HVal]
| HObj [(String, HVal)]
deriving (Show, Eq, Read)
data Op -- There's only one operator for the sake of brevity at the moment.
= Add
deriving (Show, Read)
newtype Parser a = Parser {
runParser :: String -> Maybe (String, a)
}

以下函数是我实现运算符解析器的尝试。

ops :: [Char]
ops = ['+']
isOp :: Char -> Bool
isOp c = elem c ops
spanP :: (Char -> Bool) -> Parser String
spanP f = Parser $ input -> let (token, rest) = span f input
in Just (rest, token)
opLiteral :: Parser String
opLiteral = spanP isOp
sOp :: String -> Op
sOp "+"  = Add
sOp  _   = undefined
parseOp :: Parser Op
parseOp = sOp <$> (charP '"' *> opLiteral <* charP '"')

上面的逻辑类似于字符串的解析方式,因此我的假设是,唯一的区别是专门查找运算符,而不是引号之间的数字。它似乎开始正确解析,但随后给了我以下错误:

λ > runParser parseOp ""+""
Just ("+"",*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:80:14 in base:GHC.Err
undefined, called at /DIRECTORY/parser.hs:110:11 in main:Main

我不知道错误发生在哪里。我假设它与sOp有关,主要是因为其他函数如何按预期工作,而parseOp的其余部分是parseString函数的翻译:

stringLiteral :: Parser String
stringLiteral = spanP (/= '"')  
parseString :: Parser HVal
parseString = HString <$> (charP '"' *> stringLiteral <* charP '"') 

然而,我有sOp的唯一原因是,如果它被替换为say Op,我会得到以下不存在Op :: String -> Op的错误。当我这么说的时候,我的倾向是,来自解析表达式的字符串将被传递到这个函数中,在那里我可以返回适当的运算符。然而,这是不正确的,我不知道如何继续。

charP及其应用实例

charP :: Char -> Parser Char
charP x = Parser $ f
where f (y:ys)
| y == x = Just (ys, x)
| otherwise = Nothing
f []  = Nothing
instance Applicative Parser where
pure x = Parser $ input -> Just (input, x)
(Parser p) <*> (Parser q) = Parser $ input -> do
(input', f) <- p input
(input', a) <- q input
Just (input', f a)

(<*>)的实现是罪魁祸首。您在对q的下一次调用中没有使用input',而是使用了input。结果,您将字符串传递给下一个解析器,而不需要";吃";字符。你可以用来解决这个问题

instance Applicative Parser where
pure x = Parser $ input -> Just (input, x)
(Parser p) <*> (Parser q) = Parser $ input -> do
(input', f) <- p input
(input'', a) <- qinput'
Just (input'', f a)

使用Applicative的更新实例,我们得到:

*Main> runParser parseOp ""+""
Just ("",Add)

相关内容

最新更新