packages feed

boilerplate-0.0.1: library/Boilerplate/ConfigParser.hs

{-# LANGUAGE TupleSections #-}

module Boilerplate.ConfigParser (configParser, configCommentParser) where

import Boilerplate.Types
import Control.Applicative
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import Text.Parser.Char
import Text.Parser.Combinators

-- parse the whole comment section to enhance locations in errors
configCommentParser :: CharParsing m => m Config
configCommentParser = (block <|> line) <* eof
  where
    block = between (string "{-" *> header) (try $ spaces *> string "-}") configParser
    line = string "--" *> header *> configParser <* spaces
    header = whitespace *> string "BOILERPLATE" *> whitespace
    whitespace = space *> spaces

configParser :: CharParsing m => m Config
configParser = try start <|> try end <|> config
  where
    start = ConfigStart <$ string "START"
    end = ConfigEnd <$ string "END"
    config = Config <$> conid <*> (whitespace *> rules) <*> option [] (try $ whitespace *> customs)
    whitespace = space <* spaces
    conid = (\c cs -> T.pack $ c : cs) <$> upper <*> many (noneOf " \t\n\r\f,")
    rules = sepBy1 conid (char ',' <* spaces)
    customs = sepEndBy1 custom whitespace -- consumes trailing whitespace
      where
        custom = (,) <$> (key <* spaces <* char '=' <* spaces) <*> customParser <?> "custom key/value"
        key = T.pack <$> some alphaNum <?> "custom key"

customParser :: CharParsing m => m Custom
customParser = (try nindexed <|> named <|> indexed <|> global) <?> "custom value"
  where
    nindexed = NamedIndexed <$> object array
    named = Named <$> object txt
    indexed = Indexed <$> array
    global = Global <$> txt

    object v = M.fromList <$> between (char '{' *> spaces) (spaces *> char '}') (sepBy kv comma)
      where kv = (,) <$> (symbol <* spaces <* char ':') <*> (spaces *> v)
    array = between (char '[' *> spaces) (spaces *> char ']') (sepBy txt comma)
    comma = try $ spaces *> char ',' <* spaces
    txt = quoted <|> symbol
      where quoted = T.pack <$> (many $ notChar '"') `surroundedBy` (char '"')
    symbol = T.pack <$> some alphaNum <?> "symbol"