1{-# LANGUAGE OverloadedStrings #-}23module Data.Scfg.Parser (parseConfig) where45import Control.Monad (void)6import Data.Char (isControl)7import Data.Functor (($>))8import Data.List.NonEmpty (head)9import Data.Maybe (catMaybes, fromMaybe)10import Data.Scfg.Types11import Data.Text (Text)12import qualified Data.Text as T13import Data.Void (Void)14import Text.Megaparsec hiding (ParseError)15import Text.Megaparsec.Char16import Prelude hiding (head)1718type Parser = Parsec Void Text1920parseConfig :: Text -> Either ParseError Config21parseConfig input = case parse config "" input of22 Left bundle ->23 let firstError = head (bundleErrors bundle)24 offset = errorOffset firstError25 (_, state) = reachOffset offset (bundlePosState bundle)26 SourcePos _ l c = pstateSourcePos state27 in Left28 ParseError29 { errorLine = unPos l30 , errorColumn = unPos c31 , errorMessage = T.pack (errorBundlePretty bundle)32 }33 Right c -> Right c3435config :: Parser Config36config = catMaybes <$> many line <* eof3738line :: Parser (Maybe Directive)39line = hspace *> ((newline $> Nothing) <|> (comment $> Nothing) <|> (Just <$> directive))4041comment :: Parser ()42comment = (char '#' *> takeWhileP Nothing isVChar *> newline) $> ()4344directive :: Parser Directive45directive = do46 n <- word47 params <- many (try (hspace1 *> word))48 children <- optional (try (hspace1 *> block))49 _ <- hspace *> (void newline <|> eof)50 pure (Directive n params (fromMaybe [] children))5152block :: Parser [Directive]53block = do54 _ <- char '{'55 _ <- hspace *> newline56 ds <- catMaybes <$> many (try line)57 _ <- hspace *> char '}'58 pure ds5960word :: Parser Text61word = dquoteWord <|> squoteWord <|> atom6263atom :: Parser Text64atom = T.concat <$> some (escPair <|> takeWhile1P (Just "character") isAtomChar)6566dquoteWord :: Parser Text67dquoteWord = char '"' *> (T.concat <$> many (escPair <|> takeWhile1P (Just "character") isDqChar)) <* char '"'6869squoteWord :: Parser Text70squoteWord = char '\'' *> (takeWhile1P (Just "character") isSqChar <|> pure "") <* char '\''7172escPair :: Parser Text73escPair = T.singleton <$> (char '\\' *> satisfy isVChar)7475isVChar :: Char -> Bool76isVChar c = c == '\t' || (not (isControl c) && c /= '\n')7778isAtomChar :: Char -> Bool79isAtomChar c = not (isControl c) && c /= ' ' && c /= '\n' && c /= '{' && c /= '}' && c /= '"' && c /= '\\' && c /= '\''8081isDqChar :: Char -> Bool82isDqChar c = (c == '\t' || not (isControl c)) && c /= '"' && c /= '\\' && c /= '\n'8384isSqChar :: Char -> Bool85isSqChar c = (c == '\t' || not (isControl c)) && c /= '\'' && c /= '\n'