在Haskell中使用lex解析字符串

时间:2014-01-16 12:07:40

标签: parsing haskell

我正在关注Gentle introduction to Haskell教程,其中提供的代码似乎已被破坏。我需要了解它是否如此,或者我对这个概念的看法是错误的。

我正在为自定义类型实现解析器:

data Tree a = Leaf a | Branch (Tree a) (Tree a)

便于打印功能

showsTree              :: Show a => Tree a -> String -> String
showsTree (Leaf x)     = shows x
showsTree (Branch l r) = ('<':) . showsTree l . ('|':) . showsTree r . ('>':)

instance Show a => Show (Tree a) where 
    showsPrec _ x = showsTree x

这个解析器很好但是在空格

时会中断
readsTree         :: (Read a) => String -> [(Tree a, String)]
readsTree ('<':s) =  [(Branch l r, u) | (l, '|':t) <- readsTree s,
                                        (r, '>':u) <- readsTree t ]
readsTree s       =  [(Leaf x, t)     | (x,t)      <- reads s]

这个被认为是一个更好的解决方案,但它不起作用没有空格

readsTree_lex    :: (Read a) => String -> [(Tree a, String)]
readsTree_lex s  = [(Branch l r, x) | ("<", t) <- lex s,
                                   (l, u)   <- readsTree_lex t,
                                   ("|", v) <- lex u,
                                   (r, w)   <- readsTree_lex v,
                                   (">", x) <- lex w ]
                ++
                [(Leaf x, t)     | (x, t)   <- reads s ]

接下来,我选择一个与read

一起使用的解析器
instance Read a => Read (Tree a) where
    readsPrec _ s = readsTree s

然后我使用Leksah调试模式将其加载到ghci中(这是不相关的,我猜),并尝试解析两个字符串:

    read "<1|<2|3>>"   :: Tree Int -- succeeds with readsTree
    read "<1| <2|3> >" :: Tree Int -- succeeds with readsTree_lex

lex遇到前一个字符串的|<2...部分时,它会分割到("|<", _)。这与解析器的("|", v) <- lex u部分不匹配,无法完成解析。

有两个问题出现:

  1. 如何定义真正忽略空格的解析器,而不是要求
  2. 如何定义用于使用lex
  3. 拆分遇到的文字的规则

    谈到第二个问题 - 人们更多地要求好奇,因为定义我自己的词法分析器似乎比定义现有词法分析器的规则更正确。

2 个答案:

答案 0 :(得分:4)

lex分成 Haskell 词汇,跳过空格。

这意味着由于Haskell允许|<作为词位,lex不会将其分成两个词位,因为这不是它在Haskell中解析的方式。

如果您对Haskell使用相同(或类似)的语法规则,则只能在解析器中使用lex

如果你想忽略所有空格(而不是使任何空格等同于一个空格),首次运行filter (not.isSpace)会更简单,更有效。

答案 1 :(得分:2)

对此的回答似乎是Gentle introduction to Haskell及其code samples的文字之间的小差距,加上示例代码中的错误。

还应该有一个词法分析器,但在代码库中没有工作示例(满足我的需要),所以我写了一个。请指出其中的任何缺陷:

lexAll :: ReadS String
lexAll s = case lex s of
            [("",_)] -> []                                  -- nothing to parse.
            [(c, r)] -> if length c == 1 then [(c, r)]      -- we will try to match
                           else [(c, r), ([head s], tail s)]-- not only as it was 
            any_else -> any_else                            -- parsed but also splitted

作者sais:

  

最后,完整的读者。这对白色空间不敏感   是以前的版本。为数据派生Show类时   类型自动生成的阅读器与风格类似。

但应使用lexAll而不是lex(似乎是错误):

readsTree' :: (Read a) => ReadS (Tree a)
readsTree' s = [(Branch l r, x) | ("<", t) <- lexAll s,
                  (l, u)   <- readsTree' t,
                                  ("|", v) <- lexAll u,
                                  (r, w)   <- readsTree' v,
                  (">", x) <- lexAll w ]
                ++
                [(Leaf x, t)    | (x, t) <- reads s]