├── CONTRIBUTING.md ├── Config ├── Error.idr ├── INI.idr ├── JSON.idr ├── Parse │ ├── Common.idr │ └── Utils.idr ├── Properties.idr ├── Test │ ├── INI.idr │ ├── JSON.idr │ └── YAML.idr └── YAML.idr ├── LICENSE ├── Makefile ├── README.org ├── config.ipkg └── fetch-deps.sh /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | To a certain extent, I will welcome pull requests, bug reporting, and bug squashing! 4 | However, I will not provide guarantees that I will accept your pull request or ideas. 5 | I may infact reject them, or reimplmenet your ideas as I see fit. 6 | 7 | In the case I will accept your pull request, I would prefer that we came make it as easy as possible to contribute your changes to my code base. 8 | Here are a few guidelines that I would like contributors to follow so that we can have a chance of keeping on top of things. 9 | 10 | ## Getting Started 11 | 12 | 1. Make sure you are familiar with [Git](http://git-scm.com/book). 13 | 1. Make sure you have a [GitHub account](https://github.com/signup/free). 14 | 1. Make sure you are familiar with: [Idris](http://eb.host.cs.st-andrews.ac.uk/writings/idris-tutorial.pdf). 15 | 1. Make sure you can install `Idris`: 16 | * [Mac OS X](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-OS-X-using-Homebrew) 17 | * [Ubuntu](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-Ubuntu) 18 | * [Debian](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-Debian) 19 | * [Windows](https://github.com/idris-lang/Idris-dev/wiki/Idris-on-Windows) 20 | 21 | # Issue Reporting 22 | 23 | Before you report an issue, or wish to add cool functionality please try and check to see if there are existing [issues](https://github.com/jfdm/idris-config/issues) and [pull requests](https://github.com/jfdm/idris-config/pulls). 24 | I do not want you wasting your time, duplicating somebody's work! 25 | 26 | ## The Campsite Rule 27 | 28 | A basic rule when contributing to this project is the **campsite rule**: leave the codebase in better condition than you found it. 29 | Please clean up any messes that you find, and don't leave behind new messes for the next contributor. 30 | 31 | ## Making Changes 32 | 33 | Idris developers and hackers try to adhere to something similar to the [successful git branching model](http://nvie.com/posts/a-successful-git-branching-model/). 34 | I too try to adhere to the same model, but will not guarantee that I will. 35 | The steps are straightforward. 36 | 37 | ### New contributors 38 | 39 | For those new to the project: 40 | 41 | 1. Fork the [main development repository](https://github.com/jfdm/idris-config) on github e.g. 42 | 2. Clone your fork to your local machine: 43 | 44 | ``` 45 | $ git clone git@github.com//idris-config.git 46 | ``` 47 | 48 | 3. Add `jfdm/idris-config` as a remote upstream 49 | 50 | ``` 51 | $ git remote add upstream git@github.com:jfdm/idris-config.git 52 | ``` 53 | 54 | ### Existing Contributors 55 | 56 | For those already contributing to the project: 57 | 58 | 1. Ensure your existing clone is up-to-date with current `HEAD` e.g. 59 | 60 | ``` 61 | $ git fetch upstream 62 | $ git merge upstream/master 63 | ``` 64 | 65 | ### Remaining Steps 66 | 67 | The remaining steps are the same for both new and existing contributors: 68 | 69 | 1. Create, and checkout onto, a topic branch on which to base you work. 70 | * This is typically the master branch. 71 | * For your own sanity, please avoid working on the `master` branch. 72 | 73 | ``` 74 | $ git branch fix/master/my_contrib master 75 | $ git checkout fix/master/my_contrib 76 | ``` 77 | 78 | 1. Make commits of logical units. 79 | 1. Check for unnecessary whitespace with 80 | 81 | ``` 82 | $ git diff --check 83 | ``` 84 | 85 | 1. Make sure your commit messages are along the lines of: 86 | 87 | Short (50 chars or less) summary of changes 88 | 89 | More detailed explanatory text, if necessary. Wrap it to about 72 90 | characters or so. In some contexts, the first line is treated as the 91 | subject of an email and the rest of the text as the body. The blank 92 | line separating the summary from the body is critical (unless you omit 93 | the body entirely); tools like rebase can get confused if you run the 94 | two together. 95 | 96 | Further paragraphs come after blank lines. 97 | 98 | - Bullet points are okay, too 99 | 100 | - Typically a hyphen or asterisk is used for the bullet, preceded by a 101 | single space, with blank lines in between, but conventions vary here 102 | 103 | 1. Push your changes to a topic branch in your fork of the repository. 104 | 105 | ``` 106 | $ git push origin fix/master/my_contrib 107 | ``` 108 | 109 | 1. Go to GitHub and submit a pull request to `idris-config` 110 | 111 | From there you will have to wait on my response to the request. 112 | This response might be an accept or some changes/improvements/alternatives will be suggest. 113 | We do not guarantee that all requests will be accepted. 114 | 115 | ## Increases chances of acceptance. 116 | 117 | To help increase the chance of your pull request being accepted: 118 | 119 | 1. Update the documentation, the surrounding one, examples elsewhere, guides, whatever is affected by your contribution 120 | 1. Use appropriate code formatting for `Idris` 121 | -------------------------------------------------------------------------------- /Config/Error.idr: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------------------------- [ Error.idr ] 2 | -- Module : Error.idr 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Error 7 | 8 | %access public export 9 | 10 | data ConfigError : Type where 11 | PureParseErr : String -> ConfigError 12 | ParseError : String -> String -> ConfigError 13 | FileNotFound : String -> FileError -> ConfigError 14 | 15 | Show ConfigError where 16 | show (PureParseErr err) = unlines ["Parse Error:", err] 17 | show (ParseError fn err) = 18 | unlines [ unwords ["Parse Error:", fn, "error was:"] 19 | , err 20 | ] 21 | show (FileNotFound fname err) = 22 | unlines [ unwords ["File", show fname, "caused error:"] 23 | , show err 24 | ] 25 | 26 | 27 | -- --------------------------------------------------------------------- [ EOF ] 28 | -------------------------------------------------------------------------------- /Config/INI.idr: -------------------------------------------------------------------------------- 1 | -- ----------------------------------------------------------------- [ INI.idr ] 2 | -- Description : Read INI files. 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.INI 7 | 8 | import Effects 9 | import Effect.File 10 | 11 | import public Lightyear 12 | import public Lightyear.Char 13 | import public Lightyear.Strings 14 | 15 | import public Config.Error 16 | 17 | import Config.Parse.Utils 18 | import Config.Parse.Common 19 | 20 | %access export 21 | 22 | -- ------------------------------------------------------------------- [ Model ] 23 | 24 | public export 25 | data INIElem = INIFile (List INIElem) 26 | | INIEntry String String 27 | | INISection String (List INIElem) 28 | 29 | public export 30 | Show INIElem where 31 | show (INIFile is) = show is 32 | show (INIEntry k v) = show k ++ " = " ++ show v ++ "\n" 33 | show (INISection t kvs) = "[" ++ t ++ "]\n" ++ show kvs 34 | 35 | -- ------------------------------------------------------------------ [ Parser ] 36 | 37 | private 38 | iniComment : Parser () 39 | iniComment = comment ";" <|> comment "#" 40 | 41 | private 42 | iniSpace : Parser () 43 | iniSpace = langSpace iniComment "INI Space" 44 | 45 | private 46 | kvpair : Parser INIElem 47 | kvpair = do 48 | (k,v) <- keyvalue "=" (map pack $ manyTill (anyChar) (skip endOfLine <|> iniComment)) 49 | pure $ INIEntry k v 50 | "INI KVPair" 51 | 52 | private 53 | section : Parser INIElem 54 | section = do 55 | name <- brackets word 56 | is <- some body 57 | pure $ INISection name is 58 | "Section" 59 | where 60 | body : Parser INIElem 61 | body = iniSpace *> kvpair "INI Section Body" 62 | 63 | private 64 | iniElem : Parser INIElem 65 | iniElem = kvpair <|> section "INI Elememnt" 66 | 67 | 68 | parseINI : Parser INIElem 69 | parseINI = do 70 | es <- some (iniSpace *> iniElem) 71 | pure $ INIFile es 72 | "INI File" 73 | 74 | -- -------------------------------------------------------------------- [ Read ] 75 | 76 | readINIConfig : String -> Eff (Either ConfigError INIElem) [FILE ()] 77 | readINIConfig = readConfigFile parseINI 78 | 79 | -- --------------------------------------------------------------------- [ EOF ] 80 | -------------------------------------------------------------------------------- /Config/JSON.idr: -------------------------------------------------------------------------------- 1 | -- ---------------------------------------------------------------- [ JSON.idr ] 2 | -- Description : Parse JSON files. 3 | -- This code was borrowed and improved from lightyear examples. 4 | -- 5 | -- Copyright : (c) Jan de Muijnck-Hughes 6 | -- License : see LICENSE 7 | -- --------------------------------------------------------------------- [ EOH ] 8 | module Config.JSON 9 | 10 | import public Data.AVL.Dict 11 | 12 | import Effects 13 | import Effect.File 14 | 15 | import public Lightyear 16 | import public Lightyear.Char 17 | import public Lightyear.Strings 18 | 19 | import public Config.Error 20 | 21 | import Config.Parse.Utils 22 | import Config.Parse.Common 23 | 24 | %access private 25 | 26 | -- ------------------------------------------------------------------- [ Model ] 27 | 28 | public export 29 | data JsonValue = JsonString String 30 | | JsonNumber Double 31 | | JsonBool Bool 32 | | JsonNull 33 | | JsonArray (List JsonValue) 34 | | JsonObject (Dict String JsonValue) 35 | 36 | public export 37 | Show JsonValue where 38 | show (JsonString s) = show s 39 | show (JsonNumber x) = show x 40 | show (JsonBool True ) = "true" 41 | show (JsonBool False) = "false" 42 | show JsonNull = "null" 43 | show (JsonArray xs) = show xs 44 | show (JsonObject xs) = 45 | "{" ++ unwords (intersperse "," (map fmtItem $ Dict.toList xs)) ++ "}" 46 | where 47 | fmtItem (k, v) = show k ++ " : " ++ show v 48 | 49 | -- ------------------------------------------------------------------ [ Parser ] 50 | jsonString : Parser String 51 | jsonString = quoted '"' "JSON String" 52 | 53 | jsonNumber : Parser Double 54 | jsonNumber = map scientificToFloat parseScientific "JSON Number" 55 | 56 | jsonBool : Parser Bool 57 | jsonBool = (char 't' >! string "rue" *> pure True) 58 | <|> (char 'f' >! string "alse" *> pure False) 59 | "JSON Bool" 60 | 61 | jsonNull : Parser () 62 | jsonNull = (char 'n' >! string "ull" >! pure ()) "JSON Null" 63 | 64 | mutual 65 | jsonArray : Parser (List JsonValue) 66 | jsonArray = brackets (commaSep jsonValue) "JSON Array" 67 | 68 | keyValuePair : Parser (String, JsonValue) 69 | keyValuePair = do 70 | key <- spaces *> jsonString <* spaces 71 | colon 72 | value <- jsonValue 73 | pure (key, value) 74 | "JSON KV Pair" 75 | 76 | jsonObject : Parser (Dict String JsonValue) 77 | jsonObject = map fromList $ braces (commaSep (keyValuePair)) "JSON Object" 78 | 79 | jsonValue' : Parser JsonValue 80 | jsonValue' = (map JsonString jsonString) 81 | <|> (map JsonNumber jsonNumber) 82 | <|> (map JsonBool jsonBool) 83 | <|> (pure JsonNull <* jsonNull) 84 | <|>| map JsonArray jsonArray 85 | <|>| map JsonObject jsonObject 86 | 87 | jsonValue : Parser JsonValue 88 | jsonValue = spaces *> jsonValue' <* spaces "JSON Value" 89 | 90 | export 91 | parseJSONFile : Parser JsonValue 92 | parseJSONFile = (map JsonArray jsonArray) 93 | <|> (map JsonObject jsonObject) 94 | "JSON Files" 95 | 96 | 97 | 98 | export 99 | toString : JsonValue -> String 100 | toString doc = show doc 101 | 102 | export 103 | fromString : String -> Either ConfigError JsonValue 104 | fromString str = 105 | case parse parseJSONFile str of 106 | Left err => Left (PureParseErr err) 107 | Right doc => Right doc 108 | 109 | -- -------------------------------------------------------------------- [ Read ] 110 | export 111 | readJSONConfig : String -> Eff (Either ConfigError JsonValue) [FILE ()] 112 | readJSONConfig = readConfigFile parseJSONFile 113 | 114 | -- --------------------------------------------------------------------- [ EOF ] 115 | -------------------------------------------------------------------------------- /Config/Parse/Common.idr: -------------------------------------------------------------------------------- 1 | -- -------------------------------------------------------------- [ Common.idr ] 2 | -- Description : Common Parsing Functions 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Parse.Common 7 | 8 | import Effects 9 | import Effect.File 10 | 11 | import Lightyear 12 | import Lightyear.Char 13 | import Lightyear.Strings 14 | import Lightyear.StringFile 15 | 16 | import Config.Parse.Utils 17 | import Config.Error 18 | 19 | %access export 20 | 21 | langSpace : Parser () -> Parser () 22 | langSpace p = p <|> spaces "Space Lang" 23 | 24 | keyvalue : String 25 | -> Parser String 26 | -> Parser (String, String) 27 | keyvalue s value = do 28 | k <- word 29 | spaces 30 | token s 31 | v <- value 32 | spaces 33 | pure (k,v) 34 | "KVPair" 35 | 36 | readConfigFile : Parser a 37 | -> String 38 | -> Eff (Either ConfigError a) [FILE ()] 39 | readConfigFile p f = parseFile FileNotFound ParseError p f 40 | 41 | -- --------------------------------------------------------------------- [ EOF ] 42 | -------------------------------------------------------------------------------- /Config/Parse/Utils.idr: -------------------------------------------------------------------------------- 1 | -- --------------------------------------------------------------- [ Utils.idr ] 2 | -- Description : Utility parsing functions. 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Parse.Utils 7 | 8 | import Lightyear 9 | import Lightyear.Char 10 | import Lightyear.Strings 11 | 12 | %access export 13 | 14 | -- ------------------------------------------------------------------- [ Stuff ] 15 | -- These should be merged into Lightyear 16 | 17 | word : Parser String 18 | word = map pack (some $ alphaNum) "Word" 19 | 20 | ascii : Parser Char 21 | ascii = do 22 | c <- satisfy (const True) 23 | case c of 24 | ',' => satisfy (const False) 25 | '{' => satisfy (const False) 26 | '}' => satisfy (const False) 27 | '[' => satisfy (const False) 28 | ']' => satisfy (const False) 29 | otherwise => if ord c >= 33 && ord c <= 176 30 | then pure c 31 | else satisfy (const False) 32 | 33 | asciiSeq : Parser String 34 | asciiSeq = map pack (some ascii) "Ascii String sans space and braces" 35 | 36 | comment : String -> Parser () 37 | comment str = do 38 | string str 39 | manyTill anyChar endOfLine 40 | pure () 41 | unwords ["Comment with char", str] 42 | 43 | -- ------------------------------------------------------------- [ Hex Numbers ] 44 | -- Borrowed from Lightyear JSON Examples 45 | hex : Parser Int 46 | hex = do 47 | c <- map (ord . toUpper) $ satisfy isHexDigit 48 | pure $ if c >= ord '0' && c <= ord '9' 49 | then c - ord '0' 50 | else 10 + c - ord 'A' 51 | 52 | -- Borrowed from Lightyear JSON Examples 53 | hexQuad : Parser Int 54 | hexQuad = do 55 | a <- hex 56 | b <- hex 57 | c <- hex 58 | d <- hex 59 | pure $ a * 4096 + b * 256 + c * 16 + d 60 | 61 | --Borrowed from Lightyear JSON Examples. 62 | specialChar : Parser Char 63 | specialChar = do 64 | c <- satisfy (const True) 65 | case c of 66 | '"' => pure '"' 67 | '\\' => pure '\\' 68 | '/' => pure '/' 69 | 'b' => pure '\b' 70 | 'f' => pure '\f' 71 | 'n' => pure '\n' 72 | 'r' => pure '\r' 73 | 't' => pure '\t' 74 | 'u' => map chr hexQuad 75 | _ => satisfy (const False) "expected special char" 76 | 77 | 78 | -- ------------------------------------------------------ [ Scientific Numbers ] 79 | -- Borrowed from Lightyear JSON Examples 80 | -- inspired by Haskell's Data.Scientific module 81 | 82 | public export 83 | record Scientific where 84 | constructor MkScientific 85 | coefficient : Integer 86 | exponent : Integer 87 | 88 | scientificToFloat : Scientific -> Double 89 | scientificToFloat (MkScientific c e) = fromInteger c * exp 90 | where 91 | exp = if e < 0 92 | then 1 / pow 10 (fromIntegerNat (- e)) 93 | else pow 10 (fromIntegerNat e) 94 | 95 | parseScientific : Parser Scientific 96 | parseScientific = do 97 | sign <- maybe 1 (const (-1)) `map` opt (char '-') 98 | digits <- some digit 99 | hasComma <- isJust `map` opt (char '.') 100 | decimals <- if hasComma 101 | then some digit 102 | else pure Prelude.List.Nil 103 | hasExponent <- isJust `map` opt (char 'e') 104 | exponent <- if hasExponent 105 | then integer 106 | else pure 0 107 | pure $ MkScientific (sign * fromDigits (digits ++ decimals)) 108 | (exponent - cast (length decimals)) 109 | where 110 | fromDigits : List (Fin 10) -> Integer 111 | fromDigits = foldl (\a, b => 10 * a + cast b) 0 112 | -- --------------------------------------------------------------------- [ EOF ] 113 | -------------------------------------------------------------------------------- /Config/Properties.idr: -------------------------------------------------------------------------------- 1 | -- ---------------------------------------------------------- [ Properties.idr ] 2 | -- Description : Properties files. 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Properties 7 | 8 | import Effects 9 | import Effect.File 10 | 11 | import Lightyear 12 | import Lightyear.Char 13 | import Lightyear.Strings 14 | 15 | import public Config.Error 16 | 17 | import Config.Parse.Utils 18 | import Config.Parse.Common 19 | 20 | %access private 21 | 22 | -- ------------------------------------------------------------------- [ Model ] 23 | public export 24 | data Property = PropFile (List Property) 25 | | PropEntry String String 26 | 27 | -- ------------------------------------------------------------------ [ Parser ] 28 | propComment : Parser () 29 | propComment = comment "#" <|> comment "!" "Property Comments" 30 | 31 | propSpace : Parser () 32 | propSpace = langSpace propComment "Prop Space" 33 | 34 | genKVpair : String -> Parser Property 35 | genKVpair s = do 36 | (k,v) <- keyvalue s (map pack $ manyTill anyChar (skip endOfLine <|> propComment)) 37 | pure $ PropEntry k v 38 | "Proeprty KVPair" 39 | 40 | kvpair : Parser Property 41 | kvpair = genKVpair "=" <|> genKVpair ":" 42 | 43 | export 44 | parseProperties : Parser Property 45 | parseProperties = do 46 | es <- some (propSpace *> kvpair) 47 | pure $ PropFile es 48 | "Properties File" 49 | 50 | -- -------------------------------------------------------------------- [ Read ] 51 | export 52 | readPropertiesConfig : String -> Eff (Either ConfigError Property) [FILE ()] 53 | readPropertiesConfig = readConfigFile parseProperties 54 | 55 | -- --------------------------------------------------------------------- [ EOF ] 56 | -------------------------------------------------------------------------------- /Config/Test/INI.idr: -------------------------------------------------------------------------------- 1 | -- ----------------------------------------------------------------- [ INI.idr ] 2 | -- Module : INI.idr 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Test.INI 7 | 8 | import Config.INI 9 | 10 | import Lightyear.Testing 11 | 12 | %access export 13 | 14 | -- ------------------------------------------------------------------- [ BEGIN ] 15 | 16 | runTests : IO () 17 | runTests = Testing.runTests 18 | [ parseTest "INI Test 1" 19 | parseINI 20 | """; last modified 1 April 2001 by John Doe 21 | [owner] 22 | name=John Doe 23 | organization=Acme Widgets Inc. 24 | 25 | [database] 26 | ; use IP address in case network name resolution is not working 27 | server=192.0.2.62 28 | port=143 29 | file="payroll.dat" 30 | """ 31 | 32 | , parseTest "INI Test 2" 33 | parseINI 34 | """#Comment 35 | a=b 36 | a=c 37 | 38 | [section] 39 | a=b 40 | 41 | # comment 42 | 43 | [section] 44 | 45 | b=b 46 | """ 47 | ] 48 | -- --------------------------------------------------------------------- [ EOF ] 49 | -------------------------------------------------------------------------------- /Config/Test/JSON.idr: -------------------------------------------------------------------------------- 1 | -- ---------------------------------------------------------------- [ Test.idr ] 2 | -- Module : Test.idr 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Test.JSON 7 | 8 | import Config.JSON 9 | 10 | import Lightyear.Testing 11 | 12 | %access export 13 | 14 | jsonTest1 : TestReport 15 | jsonTest1 = parseTest "JSON Test 1" 16 | parseJSONFile 17 | """{ 18 | "firstName": "John", 19 | "lastName": "Smith", 20 | "isAlive": true, 21 | "age": 25, 22 | "height_cm": 167.6, 23 | "address": { 24 | "streetAddress": "21 2nd Street", 25 | "city": "New York", 26 | "state": "NY", 27 | "postalCode": "10021-3100" 28 | }, 29 | "phoneNumbers": [ 30 | { 31 | "type": "home", 32 | "number": "212 555-1234" 33 | }, 34 | { 35 | "type": "office", 36 | "number": "646 555-4567" 37 | } 38 | ], 39 | "children": [], 40 | "spouse": null 41 | } 42 | """ 43 | 44 | jsonTest2 : TestReport 45 | jsonTest2 = parseTest "JSON Test 2" 46 | parseJSONFile 47 | """{ 48 | "$schema": "http://json-schema.org/draft-03/schema#", 49 | "name": "Product", 50 | "type": "object", 51 | "properties": { 52 | "id": { 53 | "type": "number", 54 | "description": "Product identifier", 55 | "required": true 56 | }, 57 | "name": { 58 | "type": "string", 59 | "description": "Name of the product", 60 | "required": true 61 | }, 62 | "price": { 63 | "type": "number", 64 | "minimum": 0, 65 | "required": true 66 | }, 67 | "tags": { 68 | "type": "array", 69 | "items": { 70 | "type": "string" 71 | } 72 | }, 73 | "stock": { 74 | "type": "object", 75 | "properties": { 76 | "warehouse": { 77 | "type": "number" 78 | }, 79 | "retail": { 80 | "type": "number" 81 | } 82 | } 83 | } 84 | } 85 | } 86 | """ 87 | 88 | jsonTest3 : TestReport 89 | jsonTest3 = parseTest "JSON Test 3" 90 | parseJSONFile 91 | """{ 92 | "firstName": "John", 93 | "lastName": "Smith", 94 | "isAlive": true, 95 | "age": 25, 96 | "height_cm": 167.6, 97 | "address": { 98 | "streetAddress": "21 2nd Street", 99 | "city": "New York", 100 | "state": "NY", 101 | "postalCode": "10021-3100" 102 | }, 103 | "phoneNumbers": [ 104 | { 105 | "type": "home", 106 | "number": "212 555-1234" 107 | }, 108 | { 109 | "type": "office", 110 | "number": "646 555-4567" 111 | } 112 | ], 113 | "children": [], 114 | "spouse": null 115 | } 116 | """ 117 | 118 | runTests : IO () 119 | runTests = Testing.runTests [jsonTest1, jsonTest2, jsonTest3] 120 | 121 | -- --------------------------------------------------------------------- [ EOF ] 122 | -------------------------------------------------------------------------------- /Config/Test/YAML.idr: -------------------------------------------------------------------------------- 1 | -- ---------------------------------------------------------------- [ YAML.idr ] 2 | -- Module : YAML.idr 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.Test.YAML 7 | 8 | import Config.YAML 9 | 10 | import Lightyear.Testing 11 | 12 | %access export 13 | -- ------------------------------------------------------------------- [ Begin ] 14 | 15 | yamlTest1 : TestReport 16 | yamlTest1 = parseTest "YAML Test 1" 17 | parseYAMLDoc 18 | """%YAML 1.2 19 | --- 20 | receipt: Oz Ware Purchase Invoice 21 | date: "2012 08 06" 22 | customer: {given: Dorothy, family: Gale} 23 | items: {partno: A4786, descrip: Water Bucket Filled} 24 | items: { partno: E1628, 25 | descrip: High Heeled Ruby Slippers, 26 | size: 8, 27 | price: 100.27, 28 | quantity: 1} 29 | billto: { city: East Centerville, state: KS} 30 | ... 31 | """ 32 | 33 | yamlTest2 : TestReport 34 | yamlTest2 = parseTestNot "YAML Test 2" 35 | parseYAMLDoc 36 | """# sequencer protocols for Laser eye surgery 37 | --- 38 | - step: &id001 # defines anchor label &id001 39 | instrument: Lasik 2000 40 | pulseEnergy: 5.4 41 | pulseDuration: 12 42 | repetition: 1000 43 | spotSize: 1mm 44 | 45 | - step: &id002 46 | instrument: Lasik 2000 47 | pulseEnergy: 5.0 48 | pulseDuration: 10 49 | repetition: 500 50 | spotSize: 2mm 51 | 52 | - step: *id001 # refers to the first step (with anchor &id001) 53 | - step: *id002 # refers to the second step 54 | - step: *id001 55 | - step: *id002 56 | """ 57 | 58 | yamlTest3 : TestReport 59 | yamlTest3 = parseTest "YAML Test 3" 60 | parseYAMLDoc 61 | """%YAML 1.2 62 | --- 63 | pattern: {problem: file.p, solution: file.p} 64 | pattern: {problem: file.p, solution: file.p} 65 | ... 66 | """ 67 | 68 | runTests : IO () 69 | runTests = Testing.runTests [yamlTest1, yamlTest2, yamlTest3] 70 | -- --------------------------------------------------------------------- [ EOF ] 71 | -------------------------------------------------------------------------------- /Config/YAML.idr: -------------------------------------------------------------------------------- 1 | -- ---------------------------------------------------------------- [ YAML.idr ] 2 | -- Description : Parse YAML files. 3 | -- Copyright : (c) Jan de Muijnck-Hughes 4 | -- License : see LICENSE 5 | -- --------------------------------------------------------------------- [ EOH ] 6 | module Config.YAML 7 | 8 | import Effects 9 | import Effect.File 10 | import Effect.Exception 11 | 12 | import public Lightyear 13 | import public Lightyear.Char 14 | import public Lightyear.Strings 15 | 16 | import public Config.Error 17 | 18 | import Config.Parse.Common 19 | import Config.Parse.Utils 20 | 21 | %access private 22 | 23 | -- ------------------------------------------------------------------- [ Model ] 24 | 25 | ||| YAML Documentation representation. 26 | ||| 27 | ||| There is no support for: 28 | ||| + Anchors 29 | ||| + Complex Maps 30 | ||| + Schema's 31 | ||| + User Defined tags 32 | ||| + Interesting Scalar Formats 33 | public export 34 | data YAMLNode : Type where 35 | -- Value Types 36 | YAMLNull : YAMLNode 37 | YAMLString : String -> YAMLNode 38 | YAMLInt : Int -> YAMLNode 39 | YAMLFloat : Double -> YAMLNode 40 | YAMLBool : Bool -> YAMLNode 41 | -- Node Types 42 | YAMLScalar : String -> YAMLNode 43 | YAMLSeq : List YAMLNode -> YAMLNode 44 | YAMLMap : List (YAMLNode, YAMLNode) -> YAMLNode 45 | -- Documents 46 | YAMLDoc : List (String, String) -> YAMLNode -> YAMLNode 47 | 48 | normaliseLiterals : String -> String 49 | normaliseLiterals s = s 50 | 51 | public export 52 | Show YAMLNode where 53 | -- Value Types 54 | show YAMLNull = "!!null \"null\"" 55 | show (YAMLString x) = "!!str " ++ show x 56 | show (YAMLInt i) = "!!int " ++ show i 57 | show (YAMLFloat f) = "!!float " ++ show f 58 | show (YAMLBool b) = "!!bool " ++ show b 59 | -- Node Types 60 | show (YAMLScalar s) = "!!str " ++ show (normaliseLiterals s) 61 | show (YAMLSeq ys) = "!!seq " ++ show ys 62 | show (YAMLMap ys) = "!!map " ++ "{" ++ 63 | unwords (intersperse "," (map showKV ys))++ "}" 64 | where 65 | showKV : (YAMLNode, YAMLNode) -> String 66 | showKV (k,v) = show k ++ " : " ++ show v 67 | -- Documents 68 | show (YAMLDoc _ x) = "%YAML 1.2\n---\n" ++ show x ++ "\n...\n" 69 | 70 | showUnTyped : YAMLNode -> String 71 | showUnTyped YAMLNull = "null" 72 | showUnTyped (YAMLString x) = x 73 | showUnTyped (YAMLInt i) = show i 74 | showUnTyped (YAMLFloat f) = show f 75 | showUnTyped (YAMLBool b) = show b 76 | -- Node Types 77 | showUnTyped (YAMLScalar s) = show (normaliseLiterals s) 78 | showUnTyped (YAMLSeq ys) = unwords ["[" 79 | , unwords $ intersperse "," (map showUnTyped ys) 80 | , "]"] 81 | showUnTyped (YAMLMap ys) = unwords ["{" 82 | , unwords (intersperse "," (map showUnTypedKV ys)) 83 | ,"}"] 84 | where 85 | showUnTypedKV : (YAMLNode, YAMLNode) -> String 86 | showUnTypedKV (k,v) = with List concat [showUnTyped k, ": ", showUnTyped v] 87 | -- Documents 88 | showUnTyped (YAMLDoc _ x) = unlines ["%YAML 1.2","---", showUnTyped x,"..."] 89 | 90 | public export 91 | implementation [yamlUnTyped] Show YAMLNode where 92 | show x = showUnTyped x 93 | 94 | -- ------------------------------------------------------------------ [ Parser ] 95 | 96 | -- [ Values ] 97 | yamlString : Parser YAMLNode 98 | yamlString = map YAMLString word "YAML String" 99 | 100 | yamlNull : Parser YAMLNode 101 | yamlNull = token "null" >! pure YAMLNull "YAML Null" 102 | 103 | yamlBool : Parser YAMLNode 104 | yamlBool = do token "false"; pure $ YAMLBool False 105 | <|> do token "true"; pure $ YAMLBool True 106 | "YAML Boolean" 107 | 108 | yamlFloat : Parser YAMLNode 109 | yamlFloat = map YAMLFloat $ map scientificToFloat parseScientific "YAML Floats" 110 | 111 | yamlInt : Parser YAMLNode 112 | yamlInt = do 113 | num <- map pack (some $ satisfy isDigit) 114 | pure $ YAMLInt (cast num) 115 | "YAML Int" 116 | 117 | yamlNum : Parser YAMLNode 118 | yamlNum = yamlFloat <|> yamlInt "YAML Num" 119 | 120 | -- [ Scalars ] 121 | 122 | yamlQuotedScalar : Parser YAMLNode 123 | yamlQuotedScalar = yamlQuoteGen '\'' <|> yamlQuoteGen '\"' "YAML Qoted Scalar" 124 | where 125 | yamlQuoteGen : Char -> Parser YAMLNode 126 | yamlQuoteGen c = do 127 | ws <- quoted c 128 | spaces 129 | pure $ YAMLScalar $ ws 130 | "YAML Scalar General" 131 | 132 | yamlFlowValue : Parser YAMLNode 133 | yamlFlowValue = yamlNull 134 | <|> yamlBool 135 | <|> yamlNum 136 | <|> yamlQuotedScalar 137 | <|> yamlStrs 138 | "YAML Primitives" 139 | where 140 | yamlStrs : Parser YAMLNode 141 | yamlStrs = do 142 | ws <- some $ lexeme asciiSeq 143 | pure $ YAMLString $ unwords ws 144 | 145 | -- [ Nodes ] 146 | yamlFlowSeq : Parser YAMLNode 147 | yamlFlowSeq = do 148 | xs <- brackets (commaSep (lexeme yamlFlowValue)) 149 | spaces 150 | pure $ YAMLSeq xs 151 | "YAML Flow Sequence" 152 | 153 | yamlKVPair : Parser (YAMLNode, YAMLNode) 154 | yamlKVPair = do 155 | key <- yamlString 156 | colon 157 | value <- yamlFlowValue 158 | pure $ (key, value) 159 | "YAML KV Pair Flow" 160 | 161 | yamlFlowMap : Parser YAMLNode 162 | yamlFlowMap = do 163 | xs <- braces (commaSep (lexeme yamlKVPair)) 164 | spaces 165 | pure $ YAMLMap xs 166 | "YAML Flow Map" 167 | 168 | 169 | yamlSentance : Parser YAMLNode 170 | yamlSentance = do 171 | ws <- manyTill (spaces *> word) endOfLine 172 | pure $ YAMLString $ unwords ws 173 | 174 | -- ------------------------------------------------------------------ [ Blocks ] 175 | yamlObject : Parser YAMLNode 176 | yamlObject = yamlNull <|> yamlBool <|> yamlNum <|> yamlQuotedScalar 177 | <|> yamlSentance <|> yamlFlowSeq <|> yamlFlowMap 178 | "YAMLValue" 179 | 180 | yamlBlockSeq : Parser YAMLNode 181 | yamlBlockSeq = do 182 | xs <- some (token "-" *!> yamlObject <* spaces) 183 | pure $ YAMLSeq xs 184 | "YAML List Sequence" 185 | 186 | yamlBlockKVPair : Parser (YAMLNode, YAMLNode) 187 | yamlBlockKVPair = do 188 | key <- yamlString 189 | colon 190 | spaces 191 | value <- yamlObject 192 | pure $ (key, value) 193 | "YAML Block KV Pair" 194 | 195 | yamlBlockMap : Parser YAMLNode 196 | yamlBlockMap = do 197 | xs <- some (yamlBlockKVPair <* spaces) 198 | pure $ YAMLMap xs 199 | "Map Block" 200 | 201 | yamlDirective : Parser (String, String) 202 | yamlDirective = do 203 | string "%" 204 | k <- word 205 | spaces 206 | v <- manyTill anyChar endOfLine 207 | pure $ (k, pack v) 208 | "YAML Directive" 209 | 210 | 211 | ||| This does not recognise: 212 | ||| + keep or strip options 213 | ||| + indent options 214 | ||| + Inline Comments. 215 | ||| + Scalar Blocks 216 | ||| + Complext Block Map and Block Seq, these only take flow nodes. 217 | export 218 | parseYAMLDoc : Parser YAMLNode 219 | parseYAMLDoc = do 220 | ds <- some yamlDirective 221 | token "---" 222 | b <- body 223 | opt $ token "..." 224 | pure $ YAMLDoc ds b 225 | "YAML Document" 226 | where 227 | body : Parser YAMLNode 228 | body = yamlBlockMap <|> yamlBlockSeq 229 | 230 | ||| This does not recognise: 231 | ||| + keep or strip options 232 | ||| + indent options 233 | ||| + Inline Comments. 234 | ||| + Scalar Blocks 235 | ||| + Complext Map and Seq Blocks 236 | export 237 | parseYAMLStream : Parser (List YAMLNode) 238 | parseYAMLStream = some parseYAMLDoc 239 | 240 | -- ------------------------------------------------------------------ [ String ] 241 | export 242 | toString : YAMLNode -> String 243 | toString doc = show @{yamlUnTyped} doc 244 | 245 | export 246 | toStringTyped : YAMLNode -> String 247 | toStringTyped doc = show doc 248 | 249 | ||| Requires that the file is untyped 250 | ||| 251 | ||| This does not recognise: 252 | ||| + keep or strip options 253 | ||| + indent options 254 | ||| + Inline Comments. 255 | ||| + Scalar Blocks 256 | ||| + Complext Map and Seq Blocks 257 | export 258 | fromString : String -> Either ConfigError YAMLNode 259 | fromString str = 260 | case parse parseYAMLDoc str of 261 | Left err => Left (PureParseErr err) 262 | Right doc => Right doc 263 | 264 | -- -------------------------------------------------------------------- [ Read ] 265 | 266 | ||| This does not recognise: 267 | ||| + keep or strip options 268 | ||| + indent options 269 | ||| + Inline Comments. 270 | ||| + Scalar Blocks 271 | ||| + Complext Map and Seq Blocks 272 | export 273 | readYAMLConfig : String -> Eff (Either ConfigError YAMLNode) [FILE ()] 274 | readYAMLConfig = readConfigFile parseYAMLDoc 275 | 276 | ||| This does not recognise: 277 | ||| + keep or strip options 278 | ||| + indent options 279 | ||| + Inline Comments. 280 | ||| + Scalar Blocks 281 | ||| + Complext Map and Seq Blocks 282 | export 283 | readYAMLStream : String -> Eff (List YAMLNode) [FILE ()] 284 | readYAMLStream inStr = 285 | case !(readConfigFile parseYAMLStream inStr) of 286 | Left _ => pure Nil 287 | Right ds => pure ds 288 | 289 | -- --------------------------------------------------------------------- [ EOF ] 290 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Jan de Muijnck-Hughes 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## Makefile 2 | 3 | IDRIS := idris 4 | LIB := config 5 | OPTS := 6 | 7 | .PHONY: clean lib install clobber check test doc 8 | 9 | install: lib 10 | ${IDRIS} ${OPTS} --install ${LIB}.ipkg 11 | 12 | lib: 13 | ${IDRIS} ${OPTS} --build ${LIB}.ipkg 14 | 15 | clean: 16 | ${IDRIS} --clean ${LIB}.ipkg 17 | find . -name "*~" -delete 18 | 19 | clobber : clean 20 | find . -name "*.ibc" -delete 21 | 22 | check: clobber 23 | ${IDRIS} --checkpkg ${LIB}.ipkg 24 | 25 | test: 26 | ${IDRIS} --testpkg ${LIB}.ipkg 27 | 28 | doc: 29 | ${IDRIS} --mkdoc ${LIB}.ipkg 30 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Idris Config 2 | 3 | Parsers for various configuration file formats. 4 | 5 | * Supported formats. 6 | + INI 7 | + Properties 8 | + JSON 9 | + YAML 10 | 11 | * Feature coverage. 12 | 13 | Please note that the parsers are not feature complete, and may break on fully featured configuration files. 14 | -------------------------------------------------------------------------------- /config.ipkg: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | author = Jan de Muijnck-Hughes 4 | maintainer = Jan de Muijnck-Hughes 5 | license = BSD3 but see LICENSE for more information 6 | brief = "Simple config file parser" 7 | readme = README.md 8 | version = 0.1 9 | sourceloc = git://git@github.com:jfdm/idris-config.git 10 | bugtracker = http://www.github.com/jfdm/idris-config/issues 11 | 12 | pkgs = containers 13 | , effects 14 | , lightyear 15 | , test 16 | 17 | modules = Config.Parse.Utils 18 | , Config.Parse.Common 19 | , Config.Error 20 | 21 | , Config.INI 22 | , Config.Properties 23 | , Config.JSON 24 | , Config.YAML 25 | 26 | , Config.Test.JSON 27 | , Config.Test.YAML 28 | , Config.Test.INI 29 | 30 | tests = Config.Test.JSON.runTests 31 | , Config.Test.YAML.runTests 32 | , Config.Test.INI.runTests 33 | -------------------------------------------------------------------------------- /fetch-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Creating Dir" 4 | mkdir -p dependancies/ 5 | cd dependancies/ 6 | 7 | echo "Fetching Deps for Config" 8 | 9 | echo "Fetching lightyear " 10 | git clone git@github.com:ziman/lightyear.git lightyear 11 | cd lightyear/ 12 | make install 13 | cd ../ 14 | 15 | echo "Fetching testing" 16 | git clone git@github.com:jfdm/idris-testing.git testing 17 | cd testing/ 18 | make install 19 | cd ../ 20 | 21 | echo "Fetching Containers" 22 | git clone git@github.com:jfdm/idris-containers.git containers 23 | cd containers/ 24 | make install 25 | cd ../ 26 | 27 | cd ../ 28 | --------------------------------------------------------------------------------