diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for hocon
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2020
+
+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.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+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
+OWNER 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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+# hocon
+
+Small library for [Typesafe's configuration specification](https://github.com/lightbend/config) built with parsec.
+
+```
+foo = bar
+bar = 123
+someObject {
+    wow = "this is nice!"
+}
+```
+
+While there are many configuration notations and formats around, HOCON stands in a comfortable place, albeit a bit underrated. Being a superset to JSON means it inherits its great readability, as well as extending it with some very nice features, such as comments, no need to add `"` for properties and values (unless some special character is required), digging up nested values, among others.
+
+## Sounds nice, how do I use it?
+
+First of all, import the basic stuff:
+```haskell
+import Data.HOCON
+import Text.Parser.HOCON
+```
+
+The first module exports the tree-like structure typical of JSON (if you're familiar with `aeson`, then it's really the same structure. It's not using _that_ structure because no way I'm including that just for the structure) and the accessor functions, while the second exports the parser function:
+```haskell
+parseHOCON :: String -> Either ParseError Config
+```
+
+If you've already used Typesafe's config on the JVM, then the functions will make you feel just like at home:
+```haskell
+getConfig :: String -> Config -> Maybe Config
+getNumber :: String -> Config -> Maybe Double
+getString :: String -> Config -> Maybe String
+getBoolean :: String -> Config -> Maybe Bool
+getList :: String -> Config -> Maybe [Config]
+hasPath :: String -> Config -> Bool
+```
+
+## Missing features / what's to come
+* Actually support comments
+* Multiline strings
+* Include another file and merge them
+* Object substitution
+
+Pull requests are always welcome!
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = return ()
diff --git a/hocon.cabal b/hocon.cabal
new file mode 100644
--- /dev/null
+++ b/hocon.cabal
@@ -0,0 +1,81 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.33.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 12270ead08a8273a5e886852616f4732e424ebe6ce7641968b4c92725e95f0c8
+
+name:           hocon
+version:        0.1.0.0
+description:    Please see the README on GitHub at <https://github.com/githubuser/hocon#readme>
+homepage:       https://github.com/githubuser/hocon#readme
+bug-reports:    https://github.com/githubuser/hocon/issues
+author:         Author name here
+maintainer:     example@example.com
+copyright:      2020 Author name here
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/githubuser/hocon
+
+library
+  exposed-modules:
+      Data.Bifunctor.Extra
+      Data.HOCON
+      Data.Map
+      Text.Parser.HOCON
+      Text.Parser.HOCON.Internal
+  other-modules:
+      Paths_hocon
+  hs-source-dirs:
+      src
+  build-depends:
+      MissingH <=1.4.3.0
+    , base >=4.7 && <5
+    , hspec
+    , parsec <=3.1.14.0
+    , split <=0.2.3.4
+  default-language: Haskell2010
+
+executable hocon-exe
+  main-is: Main.hs
+  other-modules:
+      Paths_hocon
+  hs-source-dirs:
+      app
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      MissingH <=1.4.3.0
+    , base >=4.7 && <5
+    , hocon
+    , hspec
+    , parsec <=3.1.14.0
+    , split <=0.2.3.4
+  default-language: Haskell2010
+
+test-suite hocon-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Data.MapSpec
+      Text.Parser.HOCON.InternalSpec
+      Text.Parser.HOCONSpec
+      Paths_hocon
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      MissingH <=1.4.3.0
+    , base >=4.7 && <5
+    , hocon
+    , hspec
+    , parsec <=3.1.14.0
+    , split <=0.2.3.4
+  default-language: Haskell2010
diff --git a/src/Data/Bifunctor/Extra.hs b/src/Data/Bifunctor/Extra.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Bifunctor/Extra.hs
@@ -0,0 +1,11 @@
+module Data.Bifunctor.Extra
+  ( module Data.Bifunctor
+  , mapValues
+  )
+where
+
+import Data.Bifunctor (Bifunctor(..))
+import Data.Map (Map)
+
+mapValues :: (b -> c) -> Map a b -> Map a c
+mapValues f = map (second f)
diff --git a/src/Data/HOCON.hs b/src/Data/HOCON.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/HOCON.hs
@@ -0,0 +1,99 @@
+module Data.HOCON
+  ( Config(..)
+  , getNode
+  , hasPath
+  , getString
+  , getNumber
+  , getList
+  , getBool
+  , isNull
+  , mapNode
+  , getConfig
+  , pretty
+  )
+where
+
+import Data.List (find)
+import Data.List.Split (splitOn)
+import Data.Map (Map)
+import Data.Maybe (isJust)
+import Data.String.Utils (join)
+
+data Config =
+  HOCONNode (Map String Config) |
+  HOCONString String |
+  HOCONNumber Double |
+  HOCONList [Config] |
+  HOCONBool Bool |
+  HOCONNull
+  deriving (Show, Eq)
+
+isNull :: Config -> Bool
+isNull HOCONNull = True
+isNull _         = False
+
+mapNode :: (Map String Config -> Config) -> Config -> Config
+mapNode f (HOCONNode nodes) = f nodes
+mapNode _ conf              = conf
+
+getNode :: String -> Config -> Maybe (Map String Config)
+getNode key conf = do
+  conf' <- getConfig key conf
+  case conf' of
+    HOCONNode nodes -> return nodes
+    _               -> Nothing
+
+getConfig :: String -> Config -> Maybe Config
+getConfig key = go (splitOn "." key)
+ where
+  go [k] conf = do
+    nodes      <- getNodes conf
+    nestedConf <- find ((== k) . fst) nodes
+    return $ snd nestedConf
+  go (k : ks) conf = do
+    nodes      <- getNodes conf
+    nestedConf <- find ((== k) . fst) nodes
+    go ks (snd nestedConf)
+
+getNumber :: String -> Config -> Maybe Double
+getNumber key conf = do
+  conf' <- getConfig key conf
+  case conf' of
+    HOCONNumber n -> return n
+    _             -> Nothing
+
+getString :: String -> Config -> Maybe String
+getString key conf = do
+  conf' <- getConfig key conf
+  case conf' of
+    HOCONString s -> return s
+    _             -> Nothing
+
+getList :: String -> Config -> Maybe [Config]
+getList key conf = do
+  conf' <- getConfig key conf
+  case conf' of
+    HOCONList l -> return l
+    _           -> Nothing
+
+getBool :: String -> Config -> Maybe Bool
+getBool key conf = do
+  conf' <- getConfig key conf
+  case conf' of
+    HOCONBool b -> return b
+    _           -> Nothing
+
+hasPath :: String -> Config -> Bool
+hasPath key = isJust . getConfig key
+
+getNodes :: Config -> Maybe [(String, Config)]
+getNodes (HOCONNode nodes) = Just nodes
+getNodes _                 = Nothing
+
+pretty :: Config -> String
+pretty HOCONNull           = "null"
+pretty (HOCONNumber n    ) = show n
+pretty (HOCONString s    ) = "\"" ++ s ++ "\""
+pretty (HOCONBool   b    ) = if b then "true" else "false"
+pretty (HOCONList   xs   ) = "[" ++ (join "," . map pretty $ xs) ++ "]"
+pretty (HOCONNode   nodes) = "{" ++ (join "," . map (\(k, node) -> "\"" ++ k ++ "\":" ++ pretty node) $ nodes) ++ "}"
diff --git a/src/Data/Map.hs b/src/Data/Map.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Map.hs
@@ -0,0 +1,24 @@
+module Data.Map
+  ( groupBy
+  , sortByKey
+  , Map
+  )
+where
+
+import Data.List (sortBy)
+
+type Map k v = [(k, v)]
+
+groupBy :: (Ord k, Eq k) => (v -> k) -> [v] -> Map k [v]
+groupBy f vs = sortBy (\(k1, _) (k2, _) -> compare k1 k2) $ go f (vs, [])
+ where
+  go :: Eq k => (v -> k) -> ([v], Map k [v]) -> Map k [v]
+  go _ ([], vs) = vs
+  go f (v : vs, acc) =
+    let
+      append key value (k, v) = if key == k then (k, value : v) else (k, v)
+      acc' = if any (\(k', _) -> k' == f v) acc then map (append (f v) v) acc else (f v, [v]) : acc
+    in go f (vs, acc')
+
+sortByKey :: (Ord k) => Map k v -> Map k v
+sortByKey = sortBy (\a b -> compare (fst a) (fst b))
diff --git a/src/Text/Parser/HOCON.hs b/src/Text/Parser/HOCON.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Parser/HOCON.hs
@@ -0,0 +1,12 @@
+module Text.Parser.HOCON
+  ( parseHOCON
+  , ParseError(..)
+  )
+where
+
+import Data.HOCON (Config(..))
+import Text.Parser.HOCON.Internal
+import Text.ParserCombinators.Parsec (ParseError, parse)
+
+parseHOCON :: String -> Either ParseError Config
+parseHOCON = parse hoconParser "hocon" . preProcessing
diff --git a/src/Text/Parser/HOCON/Internal.hs b/src/Text/Parser/HOCON/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Parser/HOCON/Internal.hs
@@ -0,0 +1,132 @@
+-- Based off of [this gist for a JSON parser using Parsec](https://gist.github.com/fero23/51f63a33d733055d53b4)
+
+{-# LANGUAGE ExistentialQuantification #-}
+
+module Text.Parser.HOCON.Internal
+  ( objectParser
+  , stringParser
+  , parseProps
+  , parseLabel
+  , arrayParser
+  , numberParser
+  , booleanParser
+  , nullParser
+  , preProcessing
+  , hoconParser
+  )
+where
+
+import Data.Bifunctor.Extra (mapValues)
+import Data.HOCON (Config(..), mapNode)
+import Data.List.Split (splitOn)
+import Data.Map (groupBy, sortByKey)
+import Data.String.Utils (replace, join, strip)
+import Text.ParserCombinators.Parsec
+  (char, Parser, alphaNum, digit, letter, noneOf, oneOf, space, string, between, sepBy, (<|>), many, many1, skipMany, try)
+
+whitespace :: Parser ()
+whitespace = skipMany space
+
+objectParser :: Parser Config
+objectParser = do
+  whitespace >> char '{' >> whitespace
+  props <- sepBy parseProps (whitespace >> (char ',' <|> char '\n') >> whitespace)
+  whitespace >> char '}' >> whitespace
+  return $ HOCONNode props
+
+parseString :: Parser String
+parseString = parseStr <|> parseOpenStr
+
+parseStr :: Parser String
+parseStr = between (char '\"') (char '\"') (many $ noneOf "\"" <|> try (string "\"\"" >> return '"'))
+
+parseOpenStr :: Parser String
+parseOpenStr = do
+  l   <- letter
+  str <- many (alphaNum <|> oneOf "._-")
+  return $ l : str
+
+parseProps :: Parser (String, Config)
+parseProps = do
+  label <- parseString
+  value <- whitespace >> (parseJsonLikeValue <|> parseObjectValue)
+  return $ splitProp label value
+ where
+  parseJsonLikeValue = do
+    (char ':' <|> char '=') >> whitespace
+    objectParser <|> arrayParser <|> booleanParser <|> nullParser <|> stringParser <|> numberParser
+  parseObjectValue = objectParser
+
+parseLabel :: Parser String
+parseLabel = do
+  whitespace
+  label <- parseString
+  whitespace >> (char ':' <|> char '=') >> whitespace
+  return label
+
+numberParser :: Parser Config
+numberParser = do
+  whitespace
+  digits <- many1 (digit <|> oneOf ".-")
+  whitespace
+  return . HOCONNumber $ read digits
+
+stringParser :: Parser Config
+stringParser = do
+  whitespace
+  str <- parseStr <|> parseOpenStr
+  whitespace
+  return $ HOCONString str
+
+booleanParser :: Parser Config
+booleanParser = do
+  whitespace
+  bool <- string "true" <|> string "false"
+  whitespace
+  return $ if bool == "true" then HOCONBool True else HOCONBool False
+
+nullParser :: Parser Config
+nullParser = do
+  whitespace >> string "null" >> whitespace
+  return HOCONNull
+
+arrayParser :: Parser Config
+arrayParser = do
+  whitespace >> char '[' >> whitespace
+  array <- sepBy
+    (objectParser <|> arrayParser <|> booleanParser <|> nullParser <|> stringParser <|> numberParser)
+    (whitespace >> char ',' >> whitespace)
+  whitespace >> char ']' >> whitespace
+  return $ HOCONList array
+
+splitProp :: String -> Config -> (String, Config)
+splitProp label value = case splitOn "." label of
+  [l         ] -> (l, value)
+  (l : nested) -> (l, join nested value)
+ where
+  join [l     ] value = HOCONNode [(l, value)]
+  join (l : ls) value = HOCONNode [(l, join ls value)]
+
+
+preProcessing :: String -> String
+preProcessing =
+  replace "\n" "," . replace "{\n" "{" . replace "\n}" "}" . replace ",\n" "," . join "\n" . map strip . splitOn "\n"
+
+hoconParser :: Parser Config
+hoconParser = objectParser <|> do
+  values <- sepBy parseProps (char ',')
+  let grouped = groupAndMerge values
+  let sorted = mapValues (mapNode (HOCONNode . sortByKey)) . sortByKey $ grouped
+  return $ HOCONNode sorted
+ where
+  groupAndMerge values = do
+    (key, mixedConfigsAndKeys) <- groupBy fst values
+    let configs = map snd mixedConfigsAndKeys
+    return (key, mergeAll configs)
+
+mergeAll :: [Config] -> Config
+mergeAll = foldl1 merge
+
+merge :: Config -> Config -> Config
+merge (HOCONNode a) (HOCONNode b) = HOCONNode (a ++ b)
+
diff --git a/test/Data/MapSpec.hs b/test/Data/MapSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/MapSpec.hs
@@ -0,0 +1,11 @@
+module Data.MapSpec where
+
+import Data.Map
+import Test.Hspec
+
+spec :: Spec
+spec =
+  describe "groupBy"
+    $          it "joins all the items whose key is the same"
+    $          groupBy length ["abc", "ab", "a", "atlas", "hades", "orpheus"]
+    `shouldBe` [(1, ["a"]), (2, ["ab"]), (3, ["abc"]), (5, ["hades", "atlas"]), (7, ["orpheus"])]
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/Text/Parser/HOCON/InternalSpec.hs b/test/Text/Parser/HOCON/InternalSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Text/Parser/HOCON/InternalSpec.hs
@@ -0,0 +1,74 @@
+module Text.Parser.HOCON.InternalSpec
+  ( spec
+  )
+where
+
+import Data.HOCON (Config(..))
+import Test.Hspec (describe, it, shouldBe, shouldSatisfy, Spec)
+import Text.Parser.HOCON.Internal (arrayParser, numberParser, objectParser, parseProps, stringParser, hoconParser, preProcessing)
+import Text.ParserCombinators.Parsec (parse)
+
+isLeft :: Either a b -> Bool
+isLeft (Left _) = True
+isLeft _        = False
+
+spec :: Spec
+spec = do
+  describe "stringParser" $ do
+    it "succeeds" $ do
+      parse stringParser "string" "\"saraza\"" `shouldBe` Right (HOCONString "saraza")
+      parse stringParser "string" "saraza" `shouldBe` Right (HOCONString "saraza")
+    it "fails" $ parse stringParser "string" "\"saraza" `shouldSatisfy` isLeft
+
+  describe "numberParser" $ it "succeeds" $ do
+    parse numberParser "number" "123456" `shouldBe` Right (HOCONNumber 123456)
+    parse numberParser "number" "123.456" `shouldBe` Right (HOCONNumber 123.456)
+
+  describe "arrayParser" $ do
+    it "succeeds" $ do
+      parse arrayParser "array" "[1,2,3]" `shouldBe` Right (HOCONList [HOCONNumber 1, HOCONNumber 2, HOCONNumber 3])
+      parse arrayParser "array" "[1,\"a\", a]" `shouldBe` Right (HOCONList [HOCONNumber 1, HOCONString "a", HOCONString "a"])
+    it "fails" $ do
+      parse arrayParser "array" "[1" `shouldSatisfy` isLeft
+      parse arrayParser "array" "[1, a?" `shouldSatisfy` isLeft
+
+  describe "parseProps" $ it "succeeds" $ do
+    parse parseProps "props" "foo: 123" `shouldBe` Right ("foo", HOCONNumber 123)
+    parse parseProps "props" "foo = \"bar\"" `shouldBe` Right ("foo", HOCONString "bar")
+    parse parseProps "props" "\"foo\":\"bar\"" `shouldBe` Right ("foo", HOCONString "bar")
+    parse parseProps "props" "foo {\nbar = baz\n}" `shouldBe` Right ("foo", HOCONNode [("bar", HOCONString "baz")])
+    parse parseProps "props" "foo.bar = baz" `shouldBe` Right ("foo", HOCONNode [("bar", HOCONString "baz")])
+
+  describe "objectParser" $ do
+    it "succeeds" $ do
+      parse objectParser "object" "{foo = bar,\"baz\": \"baz\"}"
+        `shouldBe` Right (HOCONNode [("foo", HOCONString "bar"), ("baz", HOCONString "baz")])
+      parse objectParser "object" "{}" `shouldBe` Right (HOCONNode [])
+    it "fails" $ do
+      parse objectParser "object" "{\nfoo = bar\n\"baz\": \"baz\"\n" `shouldSatisfy` isLeft
+      parse objectParser "object" "{\nfoo = bar\n\"baz\": \"baz\\n}" `shouldSatisfy` isLeft
+      parse objectParser "object" "{" `shouldSatisfy` isLeft
+
+  describe "preProcessing" $ do
+    it "replaces al commas followed by newline with just the coma" $ do
+      preProcessing "foo,\nbar,\nbaz" `shouldBe` "foo,bar,baz"
+      preProcessing "foo, bar, baz" `shouldBe` "foo, bar, baz"
+    it "replaces all opening brackets followed by newline with just the bracket" $ do
+      preProcessing "{\nfoo = bar}" `shouldBe` "{foo = bar}"
+      preProcessing "{foo = bar}" `shouldBe` "{foo = bar}"
+    it "replaces all newlines followed by closing bracket with just the bracket" $ do
+      preProcessing "{foo = bar\n}" `shouldBe` "{foo = bar}"
+      preProcessing "{foo = bar}" `shouldBe` "{foo = bar}"
+      preProcessing "{foo = bar\n  }" `shouldBe` "{foo = bar}"
+
+  describe "hoconParser" $ do
+    it "succeeds" $ do
+      parse hoconParser "hocon" "{}" `shouldBe` Right (HOCONNode [])
+      parse hoconParser "hocon" "foo = bar" `shouldBe` Right (HOCONNode [("foo", HOCONString "bar")])
+      parse hoconParser "hocon" "foo = bar,bar = 123"
+        `shouldBe` Right (HOCONNode [("bar", HOCONNumber 123), ("foo", HOCONString "bar")])
+      parse hoconParser "hocon" "foo = \"bar?!\",bar {baz = 123}"
+        `shouldBe` Right (HOCONNode [("bar", HOCONNode [("baz", HOCONNumber 123)]), ("foo", HOCONString "bar?!")])
+    it "fails" $ do
+      parse hoconParser "hocon" "{" `shouldSatisfy` isLeft
+      parse hoconParser "hocon" "foo =" `shouldSatisfy` isLeft
diff --git a/test/Text/Parser/HOCONSpec.hs b/test/Text/Parser/HOCONSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Text/Parser/HOCONSpec.hs
@@ -0,0 +1,19 @@
+module Text.Parser.HOCONSpec where
+
+import Data.HOCON
+import Test.Hspec
+import Text.Parser.HOCON (parseHOCON)
+
+isLeft :: Either a b -> Bool
+isLeft (Left _) = True
+isLeft _        = False
+
+spec :: Spec
+spec = describe "parseHOCON" $ do
+  it "works" $ do
+    parseHOCON "foo = bar" `shouldBe` Right (HOCONNode [("foo", HOCONString "bar")])
+    parseHOCON "foo = bar\nbar = 123\nbaz = \"nani?!\""
+      `shouldBe` Right (HOCONNode [("bar", HOCONNumber 123), ("baz", HOCONString "nani?!"), ("foo", HOCONString "bar")])
+  it "fails" $ do
+    parseHOCON "foo = bar\n{\n  foo = bar\n}" `shouldSatisfy` isLeft
+    parseHOCON "{" `shouldSatisfy` isLeft
