nanopass-0.0.2.0: src/Text/Parse/Stupid.hs
{-# LANGUAGE DeriveFunctor #-}
module Text.Parse.Stupid
( Sexpr(..)
, parse
, hydrateSpaces
, print
) where
import Prelude hiding (print)
import Data.Bifunctor (first)
data Sexpr a = Atom a | Combo String [Sexpr a]
deriving (Eq, Ord, Show, Read, Functor)
brackPairs :: [(String, String)]
brackPairs =
[ ( "(" , ")" )
, ( "$(" , ")" )
, ( "[" , "]" )
, ( "{" , "}" )
]
parse :: String -> Maybe [Sexpr String]
parse = fmap fst . go . tokenize
where
go :: [String] -> Maybe ([Sexpr String], [String])
go [] = Just ([], [])
go (t:ts) = case t of
close | close `elem` fmap snd brackPairs -> Just ([], t:ts)
open | Just close <- lookup open brackPairs -> do
(inner, rest) <- go ts
case rest of
t':rest' | t' == close -> (fmap . first) (Combo open inner :) (go rest')
_ -> Nothing
_ -> (fmap . first) (Atom t:) (go ts)
tokenize :: String -> [String]
tokenize input = do
line <- lines input
case line of
'#':_ -> [] -- remove comment lines
_ -> do
word <- words line
unbracket word
unbracket :: String -> [String]
unbracket = filter (not . null) . loop ""
where
loop acc "" = [reverse acc]
loop acc ('$':'(':cs) = reverse acc : "$(" : loop "" cs
loop acc (c:cs)
| c `elem` "()[]{}" = reverse acc : [c] : loop "" cs
| otherwise = loop (c:acc) cs
hydrateSpaces :: String -> String
hydrateSpaces ('\"':content) = go content
where
go [] = []
go ('\\':'\\':rest) = '\\':'\\':go rest
go ('\\':'+':rest) = ' ':go rest
go (c:rest) = c:go rest
hydrateSpaces str = str
print :: (a -> String) -> Sexpr a -> String
print f (Atom a) = f a
print f (Combo open sexprs) = case lookup open brackPairs of
Just close -> open ++ unwords (print f <$> sexprs) ++ close
Nothing -> errorWithoutStackTrace $ "Text.Parse.Stupid.print: not an open bracket " ++ show open