非常简单的sexp解析器

时间:2010-06-27 18:36:50

标签: ruby parsing haskell

对于一项任务,我们必须实现类似于一个非常基本的性别解析器,例如输入:

"((a b) ((c d) e) f)"

它将返回:

[["a", "b"], [["c", "d"], "e"], "f"]

由于这是较大任务的一部分,因此解析器仅获得有效输入(匹配的parens& c)。我在Ruby中提出了以下解决方案:

def parse s, start, stop
  tokens = s.scan(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}|\w+/)

  stack = [[]]

  tokens.each do |tok|
    case tok
    when start
      stack << []
    when stop
      stack[-2] << stack.pop
    else
      stack[-1] << tok
    end
  end

  return stack[-1][-1]
end

这可能不是最佳解决方案,但它可以胜任。

现在,我对一个惯用的Haskell解决方案感兴趣的核心功能(即我不关心lexing或选择分隔符,考虑已经lexed输入会很好),如果可能只使用“核心”haskell ,没有像parsec这样的扩展或库。

请注意,这不是作业的一部分,我只是对Haskell的做事方式感兴趣。

3 个答案:

答案 0 :(得分:6)

[["a", "b"], [["c", "d"], "e"], "f"]

haskell中没有有效类型(因为列表中的所有元素都必须在haskell中具有相同的类型),因此您需要为嵌套列表定义自己的数据结构,如下所示:

data NestedList = Value String | Nesting [NestedList]

现在,如果您有令牌列表,其中令牌被定义为data Token = LPar | RPar | Symbol String,您可以将其解析为NestedList,如下所示:

parse = fst . parse'

parse' (LPar : tokens) =
    let (inner, rest) = parse' tokens
        (next, outer) = parse' rest
    in
      (Nesting inner : next, outer)
parse' (RPar : tokens) = ([], tokens)
parse' ((Symbol str) : tokens) =
    let (next, outer) = parse' tokens in
    (Value str : next, outer)
parse' [] = ([],[])

答案 1 :(得分:4)

Haskell中的惯用方法是使用parsec进行组合分析。

网上有很多例子,包括

答案 2 :(得分:2)

虽然像Parsec这样的鸽友解析器很好,但你并不需要那么强大的力量 对于这个简单的案例。解析的经典方法是使用ReadS 来自前奏曲。这也是你给Sexp类型a的方式 Read实例。

至少对这种风格有点熟悉是件好事 解析,因为它有很多例子 标准库。

这是一个简单的解决方案,采用经典风格:

import Data.Char (isSpace)

data Sexp = Atom String | List [Sexp]
  deriving (Eq, Ord)

instance Show Sexp where
  show (Atom a ) = a
  show (List es) = '(' : unwords (map show es) ++ ")"

instance Read Sexp where
  readsPrec n (c:cs) | isSpace c = readsPrec n cs
  readsPrec n ('(':cs)           = [(List es, cs') |
                                      (es, cs') <- readMany n cs]
  readsPrec _ (')':_)            = error "Sexp: unmatched parens"
  readsPrec _ cs                 = let (a, cs') = span isAtomChar cs
                                   in [(Atom a, cs')]

readMany :: Int -> ReadS [Sexp]
readMany _ (')':cs) = [([], cs)]
readMany n cs       = [(e : es, cs'') | (e, cs') <- readsPrec n cs,
                                        (es, cs'') <- readMany n cs']

isAtomChar :: Char -> Bool
isAtomChar '(' = False
isAtomChar ')' = False
isAtomChar c   = not $ isSpace c

请注意Int参数readsPrec, 通常表示运算符优先级的,不是 在这里使用。