data SExp = A String | S [SExp] deriving Show psexp = liftM S $ parens ((psexp <|> atom) `sepEndBy` spaces) where parens = between (char '(') (char ')') atom = liftM A (literal <|> quoted) where literal = many1 (lower <|> char '_') quoted = do q; manyTill anyChar q where q = char '"'
No parser generator or lexer, just some function calls, and it reads almost how you'd describe parsing to another person ("a quoted string is a quote followed by a multiple characters up until a quote").
Here's a function that pretty-prints an SExp as a tree, with indents.
prettyP sexp = render (doc sexp) where doc (S (x:xs)) = hang (doc x) 2 $ vcat (map doc xs) doc (A string) = text string
The key to both of these is nice libraries.