packages feed

lhae-0.0.2: src/Util/Sexp.hs

module Util.Sexp ( Sexp (..),ValidSexp (..),parse,parseFile,prettyPrint
                 , validate)
    where

import Control.Applicative ((<$>))
import Text.ParserCombinators.Parsec ((<|>),(<?>),Parser)
import qualified Text.ParserCombinators.Parsec as P
import qualified Text.ParserCombinators.Parsec.Language as L
import qualified Text.ParserCombinators.Parsec.Token as T

data Sexp = Atom String
          | List [Sexp]

instance Show Sexp where
    show (Atom a) = show a
    show (List a) = "(" ++ (unwords $ map show a) ++ ")"

data ValidSexp = ValidAtom
               | ValidList [ValidSexp]
               | ValidListOf ValidSexp
               | ValidSexp

parse :: String -> Either String Sexp
parse = either (Left . show) Right . P.parse sexp ""

parseFile :: FilePath -> IO (Either String Sexp)
parseFile filePath = parse <$> readFile filePath

sexp,simpleSexp,atom,list :: Parser Sexp

sexp = do { T.whiteSpace lexer
          ; e <- simpleSexp
          ; P.eof
          ; return e
          } <?> "s-expression"

simpleSexp = atom <|> list <?> "simple s-expression"

atom = (Atom <$> string) <|> (Atom <$> identifier) <?> "atom"

list = List <$> (P.between (symbol "(") (symbol ")") 
                      $ P.many simpleSexp) <?> "list"

symbol = T.symbol lexer
string = T.stringLiteral lexer
identifier = T.identifier lexer

lexer :: T.TokenParser ()
lexer = T.makeTokenParser $ L.emptyDef 

prettyPrint :: Sexp -> String
prettyPrint =
    let isAtomList (List l) = 
            let isAtom (Atom _) = True
                isAtom _ = False
            in
              and $ map isAtom l
        isAtomList _ = False

        ppList space (List l) =
            let lines = map (pp $ "  " ++ space) l
            in
              unlines ([space ++ "("] ++ lines) ++ space ++ ")"

        pp space sexp = 
            if isAtomList sexp 
            then space ++ (show sexp)
            else case sexp of
                   List _ -> ppList space sexp
                   Atom a -> space ++ (show a)
    in
      pp ""

validate :: ValidSexp -> Sexp -> Bool
validate valid sexp =
    case (valid,sexp) of
      (ValidAtom,Atom _) -> True
      (ValidList v,List l) -> 
          if length v /= length l then False
          else and $ map (uncurry validate) $ zip v l
      (ValidListOf v,List l) -> and $ map (validate v) l
      (ValidSexp,_) -> True
      _ -> False