Konf (empty) → 0.1.0.0
raw patch · 4 files changed
+219/−0 lines, 4 filesdep +basedep +containersdep +parsecsetup-changed
Dependencies added: base, containers, parsec
Files
- Konf.cabal +21/−0
- LICENSE +24/−0
- Setup.hs +3/−0
- Src/Text/Konf.hs +171/−0
+ Konf.cabal view
@@ -0,0 +1,21 @@+name: Konf+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.1.0.0+synopsis: A configuration language and a parser.+description: Konf is a minimal konfiguration language, and ``Text.Konf'' is an implementation of this language.+category: Unix, Configuration+homepage: http://www.gkayaalp.com/p/konf.html+license: OtherLicense+license-file: LICENSE+author: Göktuğ Kayaalp+maintainer: self@gkayaalp.com+category: Text+build-type: Simple+cabal-version: >=1.8+library+ hs-source-dirs: Src+ exposed-modules: Text.Konf+ build-depends: base ==4.6.*, parsec ==3.1.*, containers ==0.5.*+
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2013 Göktuğ Kayaalp <self@gkayaalp.com>++All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:++ Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++ Redistributions in binary form must reproduce the above copyright notice, this+ list of conditions and the following disclaimer in the documentation and/or+ other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,3 @@+import Distribution.Simple++main = defaultMain
+ Src/Text/Konf.hs view
@@ -0,0 +1,171 @@+--------------------------------------------------------------------------------+-- |+-- Module : Text.Konf+-- Copyright : (C) 2013 Göktuğ Kayaalp+-- License : Two Clause BSD License (see file LICENSE)+--+-- Maintainer : Göktuğ Kayaalp <self@gkayaalp.com>+-- Stability : Experimental+-- Portability : Not known+--------------------------------------------------------------------------------++{-# LANGUAGE DeriveDataTypeable #-}++-- |+-- @Text.Konf@ is an implementation of the Konf language.+module Text.Konf+( Konf+, KonfValue(..)+, KonfException(..)+, konf+, konfFile+, lookup ) where++import Prelude hiding (lookup)+import Text.ParserCombinators.Parsec+import Text.Parsec.Error+import qualified Control.Exception as CE+import qualified Control.Monad as CM+import qualified Data.Map.Lazy as DML+import qualified Data.Typeable as DT++-- |+-- This data type enumerates the possible types a returned value may be.+-- Any value will be returned as a @KonfValue value@.+data KonfValue = KonfString String+ | KonfNumber Float+ | KonfComplex Float Float+ | KonfBoolean Bool+ | KonfIdentifier String+ | KonfList [KonfValue]+ | KonfUndefined+ deriving Show++-- |+-- The @Konf@ data type, a map from strings to values. This is actually a+-- parameterized @Data.Map.Lazy.Map@ and it is possible to use method in that+-- module on this type.+type Konf = DML.Map String KonfValue++data KonfException = SyntaxError String+ deriving (Show, DT.Typeable)++instance CE.Exception KonfException++parseIdentifier :: Parser String+parseIdentifier = letter >>= \initial -> + many alphaNum >>= \rest -> return (initial : rest)++parseFloat :: Parser KonfValue+parseFloat = many1 digit >>= \d ->+ -- `optionMaybe p' tries `p', if fails w/o consuming input,+ -- returns `Nothing'. If a number lacks a decimal part (this+ -- parser return `Nothing'), it is appended `.0', thus+ -- combining the parsing of numbers in one parser and making it+ -- possible the complex number literal include integers w/o+ -- modifying it.+ optionMaybe decimalPart >>= \f ->+ return $ KonfNumber $ read+ (case f of+ Just dp -> (d ++ "." ++ dp)+ Nothing -> (d ++ ".0"))+ -- Parse `.' here to let the parser fail w/o consuming input if the+ -- literal has no decimal part.+ where decimalPart = char '.' >> many1 digit -- >>= return++parseComplex :: Parser KonfValue+parseComplex = parseFloat >>= \ (KonfNumber r) ->+ skip >> char '+' >> skip >> parseFloat >>= \ (KonfNumber i) ->+ (char 'i' <|> char 'j') >> return (KonfComplex r i)+ where skip = skipMany $ oneOf " \t"++parseNumber :: Parser KonfValue+parseNumber = try parseComplex <|> try parseFloat+ +parseBoolean :: Parser KonfValue+parseBoolean = true <|> false+ where+ true = string "true" >> return (KonfBoolean True)+ false = string "false" >> return (KonfBoolean False)++parseString :: Parser KonfValue+parseString = begin >>= \open ->+ many (noneOf "\\\"'" <|> escape) >>= \str ->+ end open >> (return . KonfString) str+ where begin = oneOf "\"'"+ end character = char character+ escapeChar 'n' = '\n'+ escapeChar 't' = '\t'+ escapeChar 'r' = '\r'+ escapeChar 'b' = '\b'+ escapeChar e = e+ escape = char '\\' >> letter >>= return . escapeChar++parseIdentifierRValue :: Parser KonfValue+parseIdentifierRValue = return . KonfIdentifier =<< parseIdentifier++parseList :: Parser KonfValue+parseList = open >> sepBy value comma >>= \l ->+ close >> return (KonfList l)+ where skip = skipMany $ oneOf " \t\n"+ open = char '(' >> skip >> optional comment >> skip+ close = skip >> optional comment >> skip >> char ')'+ value = parseValue >>= \v ->+ skip >> optional comment >> skip >> return v+ comma = char ',' >> skip >> optional comment >> skip++comment :: Parser ()+comment = CM.void (char '#' >> manyTill anyChar newline)++parseValue :: Parser KonfValue+parseValue = parseList <|> parseString <|> parseNumber <|> try parseBoolean <|> parseIdentifierRValue++parsePair :: Parser (String, KonfValue)+parsePair = identifier >>= \key -> + equals >> parseValue >>= \value ->+ skip >> return (key, value)+ where skip = many $ oneOf " \t"+ equals = skip >> char '=' >> skip+ identifier = skip >> parseIdentifier++parsePairs :: Parser [(String, KonfValue)]+parsePairs = many getPair+ where skip = comment <|> CM.void newline+ terminate = (comment <|> CM.void (many1 newline)) >> optional eof+ getPair = many skip >> parsePair >>= \pair ->+ terminate >> return pair++showErr :: ParseError -> String+showErr = show++-- |+-- Parse the string and return the resulting @Konf@ object, that can be+-- looked up for keys by using the @lookup@ function.+konf :: String -- ^ A string in the language of Konf.+ -> Konf -- ^ A specialized map, as the result of parsing.+konf file = DML.fromList pairs+ -- HACK: I bet appending a newline to the end of the string is the silliest hack ever.+ where pairs = case parse parsePairs "konf" (file ++ "\n") of+ -- TODO: Don't `show' here, build the string by the error resources of parsec.+ Left err -> CE.throw $ SyntaxError ("Syntax error: " ++ showErr err)+ Right kvlist -> kvlist++-- |+-- Read a file and parse; then return the result as a @Konf@ object in the IO monad.+konfFile :: FilePath -> IO Konf+konfFile f = do str <- readFile f+ return $ konf str+++-- |+-- Lookup keys in the Konf object.+--+-- This is actually @Data.Map.Lazy.lookup@, but it additionally handles+-- mapped to other identifiers, by recursively looking those keys up+-- until it finds a value.+lookup :: Konf -> String -> KonfValue+lookup k key = case DML.lookup key k of+ Just (KonfIdentifier i) -> lookup k i+ Just value -> value+ Nothing -> KonfUndefined+