packages feed

angel-0.2.1: Angel/Parse.hs

-- |Parse Angel configuration files--a parsec parser
module Angel.Parse where

import Data.String.Utils (strip)
import Text.ParserCombinators.Parsec
import Data.Maybe (isJust)
import Data.Either (Either(..))
import Control.Monad (foldM)

import Angel.Data

type Kw = Maybe (String, String)

reqInt = manyTill (oneOf ['0'..'9']) (char '\n') <?> "integer"
configString = manyTill anyChar $ char '\n'

configLine :: GenParser Char st Kw
configLine = do 
    name <- manyTill (noneOf "[") (char ' ')
    val <- case name of 
            "exec" -> configString
            "delay" -> reqInt
            "stdout" -> configString
            "stderr" -> configString
            "directory" -> configString
            otherwise -> fail $ "unknown config verb '" ++ name ++ "'"
    return $ Just (strip name, strip val)

commentLine :: GenParser Char st Kw
commentLine = do 
    manyTill (oneOf " \t") $ char '#'
    manyTill anyChar $ char '\n'
    return Nothing

emptyLine :: GenParser Char st Kw
emptyLine = manyTill (oneOf " \t\r") (char '\n') >> return Nothing

header = do 
    char '[' <?> "start program id"
    name <- manyTill anyChar $ char ']'
    manyTill (oneOf " \t\r") $ char '\n'
    return name

program = do 
    given_id <- header
    kw <- many $ (emptyLine <|> commentLine <|> configLine)
    let real_kw = filter isJust kw
    let prg = defaultProgram{name=given_id}
    prg <- foldM setAttr prg real_kw
    mapM_ (check_set prg) [("exec", exec), ("name", name)] 
    return prg

    where 
    
        setAttr prg (Just (n, v))  = case n of 
                                    "exec" -> return prg{exec=v}
                                    "delay" -> return prg{delay=(read v)::Int}
                                    "stdout" -> return prg{stdout=v}
                                    "stderr" -> return prg{stderr=v}
                                    "directory" -> return prg { workingDir = Just v }
                                    otherwise -> fail $ "unknown config keyword " ++ n
        setAttr _ _ = fail "non-just in setAttr argument??"

        check_set prg (name, f) = if f prg == f defaultProgram 
                            then fail $ name ++ " must be set for all programs" 
                            else return ()

configFile :: GenParser Char st Spec
configFile = do 
    many $ (emptyLine <|> commentLine)
    return =<< manyTill program eof

parseConfig :: String -> Either ParseError Spec
parseConfig input = parse configFile "(config)" input