簡體   English   中英

非常簡單的sexp解析器

[英]Very simple sexp parser

對於一個賦值,我們必須實現像一個非常基本的sexp解析器,這樣的輸入,如:

"((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的處理方式感興趣。

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

haskell中沒有有效類型(因為列表中的所有元素都必須在haskell中具有相同的類型),因此您需要為嵌套列表定義自己的數據結構,如下所示:

data NestedList = Value String | Nesting [NestedList]

現在,如果你有令牌列表,其中Token被定義為data Token = LPar | RPar | Symbol String data Token = LPar | RPar | Symbol String 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' [] = ([],[])

Haskell中慣用的方法是使用parsec進行組合分析。

網上有很多例子,包括

雖然像Parsec這樣的發燒友解析器很不錯,但是對於這個簡單的情況你並不需要那么強大的功能。 解析的經典方法是使用Prelude中的ReadS類型。 這也是你將Sexp類型作為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

請注意,此處不使用通常表示運算符優先級的readsPrecInt參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM