Very simple parser sexp

For the assignment, we had to implement something like a very simple sexp parser, for example, for input:

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

He will return:

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

Since this was part of a larger purpose, only valid input (corresponding to parens & c) is provided to the parser. I came up with the following solution in 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

This may not be the best solution, but it does the job.

Now I am interested in the Haskell idiomatic solution for the main functionality (that is, I don’t care about lexing or the choice of delimiters, if you take the lexical input already it will be fine), if possible, using only the haskell core, without extensions or libs such as parsec.

Note that this is NOT part of the assignment, I'm just interested in the way Haskell does things.

+3
3
[["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' [] = ([],[])
+6

Haskell parsec, .

,

+4

, Parsec, , . 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

, Int readsPrec, , .

+2

Source: https://habr.com/ru/post/1751991/


All Articles