packages feed

derulo (empty) → 0.0.0

raw patch · 8 files changed

+573/−0 lines, 8 filesdep +basedep +derulodep +doctestsetup-changed

Dependencies added: base, derulo, doctest

Files

+ CHANGELOG.markdown view
@@ -0,0 +1,7 @@+# Change log
+
+Derulo uses [Semantic Versioning][].
+The change log is available through the [releases on GitHub][].
+
+[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
+[releases on GitHub]: https://github.com/tfausak/derulo/releases
+ LICENSE.markdown view
@@ -0,0 +1,21 @@+# [The MIT License](https://opensource.org/licenses/MIT)
+
+Copyright 2017 Taylor Fausak
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+ README.markdown view
@@ -0,0 +1,3 @@+# Derulo
+
+Derulo parses and renders JSON simply.
+ Setup.hs view
@@ -0,0 +1,4 @@+import qualified Distribution.Simple as Cabal++main :: IO ()+main = Cabal.defaultMain
+ derulo.cabal view
@@ -0,0 +1,49 @@+-- This file has been generated from package.yaml by hpack version 0.17.1.+--+-- see: https://github.com/sol/hpack++name:           derulo+version:        0.0.0+synopsis:       Parse and render JSON simply.+description:    Derulo parses and renders JSON simply.+category:       JSON+maintainer:     Taylor Fausak+license:        MIT+license-file:   LICENSE.markdown+build-type:     Simple+cabal-version:  >= 1.10++extra-source-files:+    CHANGELOG.markdown+    README.markdown++library+  hs-source-dirs:+      library+  ghc-options: -Wall+  build-depends:+      base >=4.9.1 && <4.10+  exposed-modules:+      Derulo+  default-language: Haskell2010++executable derulo+  main-is: Main.hs+  hs-source-dirs:+      executables+  ghc-options: -Wall -rtsopts -threaded+  build-depends:+      base >=4.9.1 && <4.10+    , derulo+  default-language: Haskell2010++test-suite test+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs:+      tests+  ghc-options: -Wall -rtsopts -threaded+  build-depends:+      base >=4.9.1 && <4.10+    , doctest >=0.11.4 && <0.12+  default-language: Haskell2010
+ executables/Main.hs view
@@ -0,0 +1,5 @@+import qualified Data.Maybe as Maybe+import qualified Derulo++main :: IO ()+main = interact (Derulo.showJSON . Maybe.fromJust . Derulo.readJSON)
+ library/Derulo.hs view
@@ -0,0 +1,480 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- | Derulo parses and renders JSON simply. It aims to provide an RFC 7159+-- compliant parser and renderer without incurring any dependencies. It is+-- intended to be used either for learning or in situations where dependencies+-- are unwanted. In normal usage, prefer a faster, more robust library like+-- [Aeson](https://hackage.haskell.org/package/aeson).+--+-- Derulo does not export any identifiers that conflict with the prelude and+-- can be imported unqualified.+--+-- >>> import Derulo+--+-- Use 'readJSON' to parse a 'String' into a 'JSON' value.+--+-- >>> readJSON " null "+-- Just Null+--+-- Use 'showJSON' to render a 'JSON' value as a 'String'.+--+-- >>> showJSON Null+-- "null"+module Derulo where++import qualified Control.Monad as Monad+import qualified Data.Data as Data+import qualified Data.Functor as Functor+import qualified Data.List as List+import qualified Data.Maybe as Maybe+import qualified GHC.Generics as Generics+import qualified Text.ParserCombinators.ReadP as ReadP++-- * Types+-- | A JSON value as described by RFC 7159.+data JSON+  = Null+  | Boolean Bool+  | Number Integer+           Integer+  | String String+  | Array [JSON]+  | Object [(String, JSON)]+  deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show, Data.Typeable)++-- * Parsing+-- | Parses a string as JSON.+--+-- >>> readJSON "null"+-- Just Null+--+-- >>> readJSON "true"+-- Just (Boolean True)+-- >>> readJSON "false"+-- Just (Boolean False)+--+-- >>> readJSON "0e0"+-- Just (Number 0 0)+-- >>> readJSON "12e34"+-- Just (Number 12 34)+-- >>> readJSON "-12e-34"+-- Just (Number (-12) (-34))+--+-- >>> readJSON "\"\""+-- Just (String "")+-- >>> readJSON "\"js\""+-- Just (String "js")+-- >>> readJSON "\"\\\"\\\\\\b\\f\\n\\r\\t\""+-- Just (String "\"\\\b\f\n\r\t")+-- >>> readJSON "\"\\u001f\""+-- Just (String "\US")+--+-- >>> readJSON "[]"+-- Just (Array [])+-- >>> readJSON "[null]"+-- Just (Array [Null])+-- >>> readJSON "[true,false]"+-- Just (Array [Boolean True,Boolean False])+--+-- >>> readJSON "{}"+-- Just (Object [])+-- >>> readJSON "{\"\":null}"+-- Just (Object [("",Null)])+-- >>> readJSON "{\"t\":true,\"f\":false}"+-- Just (Object [("t",Boolean True),("f",Boolean False)])+readJSON :: String -> Maybe JSON+readJSON string = runParser pJSON string++pJSON :: ReadP.ReadP JSON+pJSON = do+  pWhitespaces+  value <- pValue+  ReadP.eof+  pure value++pValue :: ReadP.ReadP JSON+pValue = ReadP.choice [pNull, pBoolean, pNumber, pString, pArray, pObject]++pNull :: ReadP.ReadP JSON+pNull = do+  pSymbol "null"+  pure Null++pBoolean :: ReadP.ReadP JSON+pBoolean = pTrue ReadP.+++ pFalse++pTrue :: ReadP.ReadP JSON+pTrue = do+  pSymbol "true"+  pure (Boolean True)++pFalse :: ReadP.ReadP JSON+pFalse = do+  pSymbol "false"+  pure (Boolean False)++pNumber :: ReadP.ReadP JSON+pNumber = do+  integer <- pInteger+  (fraction, precision) <- ReadP.option (0, 0) pFraction+  power <- ReadP.option 0 pPower+  pWhitespaces+  let mantissa = integer * 10 ^ precision + negateIf (integer <= 0) fraction+  let magnitude = power - precision+  pure (Number mantissa magnitude)++pInteger :: ReadP.ReadP Integer+pInteger = pZero ReadP.+++ pNonZero++pZero :: ReadP.ReadP Integer+pZero = do+  ReadP.optional (ReadP.char '-')+  Functor.void (ReadP.char '0')+  pure 0++pNonZero :: ReadP.ReadP Integer+pNonZero = do+  sign <- ReadP.option '+' (ReadP.char '-')+  first <- ReadP.satisfy isNonZeroDigit+  rest <- ReadP.munch isDecimalDigit+  case fromDecimal (first : rest) of+    Nothing -> ReadP.pfail+    Just nonZero -> pure (negateIf (sign == '-') nonZero)++pFraction :: ReadP.ReadP (Integer, Integer)+pFraction = do+  Functor.void (ReadP.char '.')+  digits <- ReadP.munch1 isDecimalDigit+  case fromDecimal digits of+    Nothing -> ReadP.pfail+    Just fraction -> pure (fraction, List.genericLength digits)++pPower :: ReadP.ReadP Integer+pPower = do+  Functor.void (ReadP.char 'E' ReadP.+++ ReadP.char 'e')+  sign <- ReadP.option '+' (ReadP.char '+' ReadP.+++ ReadP.char '-')+  digits <- ReadP.munch1 isDecimalDigit+  case fromDecimal digits of+    Nothing -> ReadP.pfail+    Just magnitude -> pure (negateIf (sign == '-') magnitude)++pString :: ReadP.ReadP JSON+pString = do+  string <-+    ReadP.between+      (ReadP.char '"')+      (ReadP.char '"')+      (do characters <- ReadP.many pCharacter+          pure (String characters))+  pWhitespaces+  pure string++pCharacter :: ReadP.ReadP Char+pCharacter = pLiteral ReadP.+++ pEscape++pLiteral :: ReadP.ReadP Char+pLiteral = ReadP.satisfy isLiteral++pEscape :: ReadP.ReadP Char+pEscape = do+  Functor.void (ReadP.char '\\')+  escape <- ReadP.get+  case escape of+    '"' -> pure '"'+    '/' -> pure '/'+    '\\' -> pure '\\'+    'b' -> pure '\b'+    'f' -> pure '\f'+    'n' -> pure '\n'+    'r' -> pure '\r'+    't' -> pure '\t'+    'u' -> do+      digits <- ReadP.count 4 (ReadP.satisfy isHexadecimalDigit)+      case fromHexadecimal digits of+        Nothing -> ReadP.pfail+        Just point -> pure (toEnum (fromIntegral point))+    _ -> ReadP.pfail++pArray :: ReadP.ReadP JSON+pArray =+  ReadP.between+    (pSymbol "[")+    (pSymbol "]")+    (do values <- ReadP.sepBy pValue (pSymbol ",")+        pure (Array values))++pObject :: ReadP.ReadP JSON+pObject =+  ReadP.between+    (pSymbol "{")+    (pSymbol "}")+    (do pairs <- ReadP.sepBy pPair (pSymbol ",")+        pure (Object pairs))++pPair :: ReadP.ReadP (String, JSON)+pPair = do+  String key <- pString+  pSymbol ":"+  value <- pValue+  pure (key, value)++-- * Rendering+-- | Renders JSON as a string.+--+-- >>> showJSON Null+-- "null"+--+-- >>> showJSON (Boolean True)+-- "true"+-- >>> showJSON (Boolean False)+-- "false"+--+-- >>> showJSON (Number 0 0)+-- "0e0"+-- >>> showJSON (Number 12 34)+-- "12e34"+-- >>> showJSON (Number (-12) (-34))+-- "-12e-34"+--+-- >>> showJSON (String "")+-- "\"\""+-- >>> showJSON (String "js")+-- "\"js\""+-- >>> showJSON (String "\"\\\b\f\n\r\t")+-- "\"\\\"\\\\\\b\\f\\n\\r\\t\""+-- >>> showJSON (String "\x1f")+-- "\"\\u001f\""+--+-- >>> showJSON (Array [])+-- "[]"+-- >>> showJSON (Array [Null])+-- "[null]"+-- >>> showJSON (Array [Boolean True, Boolean False])+-- "[true,false]"+--+-- >>> showJSON (Object [])+-- "{}"+-- >>> showJSON (Object [("", Null)])+-- "{\"\":null}"+-- >>> showJSON (Object [("t", Boolean True), ("f", Boolean False)])+-- "{\"t\":true,\"f\":false}"+showJSON :: JSON -> String+showJSON json = sJSON json ""++sJSON :: JSON -> ShowS+sJSON json =+  case json of+    Null -> sNull+    Boolean boolean -> sBoolean boolean+    Number mantissa magnitude -> sNumber mantissa magnitude+    String string -> sString string+    Array array -> sArray array+    Object object -> sObject object++sNull :: ShowS+sNull = showString "null"++sBoolean :: Bool -> ShowS+sBoolean boolean =+  if boolean+    then sTrue+    else sFalse++sTrue :: ShowS+sTrue = showString "true"++sFalse :: ShowS+sFalse = showString "false"++sNumber :: Integer -> Integer -> ShowS+sNumber mantissa magnitude = shows mantissa . showChar 'e' . shows magnitude++sString :: String -> ShowS+sString string =+  sSeparatedBetween (showChar '"') (showChar '"') id sCharacter string++sCharacter :: Char -> ShowS+sCharacter character =+  case character of+    '"' -> showString "\\\""+    '\\' -> showString "\\\\"+    '\b' -> showString "\\b"+    '\f' -> showString "\\f"+    '\n' -> showString "\\n"+    '\r' -> showString "\\r"+    '\t' -> showString "\\t"+    _ ->+      if isControl character+        then showString "\\u" .+             showString+               (padLeft+                  4+                  '0'+                  (toHexadecimal (fromIntegral (fromEnum character))))+        else showChar character++sArray :: [JSON] -> ShowS+sArray array =+  sSeparatedBetween (showChar '[') (showChar ']') (showChar ',') sJSON array++sObject :: [(String, JSON)] -> ShowS+sObject object =+  sSeparatedBetween (showChar '{') (showChar '}') (showChar ',') sPair object++sPair :: (String, JSON) -> ShowS+sPair (key, value) = sString key . showChar ':' . sJSON value++-- * Helpers+fromBase :: Integer -> (Char -> Maybe Integer) -> String -> Maybe Integer+fromBase b f s =+  Monad.foldM+    (\n c -> do+       d <- f c+       pure (b * n + d))+    0+    s++fromDecimal :: String -> Maybe Integer+fromDecimal s = fromBase 10 fromDecimalDigit s++fromDecimalDigit :: Char -> Maybe Integer+fromDecimalDigit c =+  case c of+    '0' -> Just 0+    '1' -> Just 1+    '2' -> Just 2+    '3' -> Just 3+    '4' -> Just 4+    '5' -> Just 5+    '6' -> Just 6+    '7' -> Just 7+    '8' -> Just 8+    '9' -> Just 9+    _ -> Nothing++fromHexadecimal :: String -> Maybe Integer+fromHexadecimal s = fromBase 16 fromHexadecimalDigit s++fromHexadecimalDigit :: Char -> Maybe Integer+fromHexadecimalDigit c =+  case c of+    'A' -> Just 10+    'B' -> Just 11+    'C' -> Just 12+    'D' -> Just 13+    'E' -> Just 14+    'F' -> Just 15+    'a' -> Just 10+    'b' -> Just 11+    'c' -> Just 12+    'd' -> Just 13+    'e' -> Just 14+    'f' -> Just 15+    _ -> fromDecimalDigit c++isControl :: Char -> Bool+isControl c = '\x00' <= c && c <= '\x1f'++isDecimalDigit :: Char -> Bool+isDecimalDigit c = '0' <= c && c <= '9'++isHexadecimalDigit :: Char -> Bool+isHexadecimalDigit c =+  isDecimalDigit c || 'A' <= c && c <= 'F' || 'a' <= c && c <= 'f'++isLiteral :: Char -> Bool+isLiteral c = not (c == '"' || c == '\\' || isControl c)++isNonZeroDigit :: Char -> Bool+isNonZeroDigit c = '1' <= c && c <= '9'++isWhitespace :: Char -> Bool+isWhitespace c = c == '\t' || c == '\n' || c == '\r' || c == ' '++negateIf :: Bool -> Integer -> Integer+negateIf p n =+  if p+    then negate n+    else n++pSymbol :: String -> ReadP.ReadP ()+pSymbol s = do+  Functor.void (ReadP.string s)+  pWhitespaces++pWhitespaces :: ReadP.ReadP ()+pWhitespaces = Functor.void (ReadP.munch isWhitespace)++padLeft :: Integer -> a -> [a] -> [a]+padLeft n x ys = reverse (padRight n x (reverse ys))++padRight :: Integer -> a -> [a] -> [a]+padRight n x ys =+  if n <= 0+    then ys+    else case ys of+           [] -> x : padRight (n - 1) x ys+           y:zs -> y : padRight (n - 1) x zs++runParser :: ReadP.ReadP a -> String -> Maybe a+runParser p s =+  Maybe.listToMaybe+    (Maybe.mapMaybe+       (\(x, t) ->+          if null t+            then Just x+            else Nothing)+       (ReadP.readP_to_S p s))++sBetween :: ShowS -> ShowS -> (anything -> ShowS) -> anything -> ShowS+sBetween left right render it = left . render it . right++sSeparated :: ShowS -> (element -> ShowS) -> [element] -> ShowS+sSeparated separator render elements =+  case elements of+    [] -> id+    [element] -> render element+    element:rest ->+      render element . separator . sSeparated separator render rest++sSeparatedBetween ::+     ShowS -> ShowS -> ShowS -> (element -> ShowS) -> [element] -> ShowS+sSeparatedBetween left right separator render elements =+  sBetween left right (sSeparated separator render) elements++toBase :: Integer -> (Integer -> Maybe Char) -> Integer -> String+toBase b f n =+  if n == 0+    then [Maybe.fromJust (f n)]+    else reverse (toBase' b f n)++toBase' :: Integer -> (Integer -> Maybe Char) -> Integer -> String+toBase' b f n =+  case quotRem n b of+    (0, 0) -> ""+    (q, r) -> Maybe.fromJust (f r) : toBase' b f q++toHexadecimal :: Integer -> String+toHexadecimal n = toBase 16 toHexadecimalDigit n++toHexadecimalDigit :: Integer -> Maybe Char+toHexadecimalDigit n =+  case n of+    0 -> Just '0'+    1 -> Just '1'+    2 -> Just '2'+    3 -> Just '3'+    4 -> Just '4'+    5 -> Just '5'+    6 -> Just '6'+    7 -> Just '7'+    8 -> Just '8'+    9 -> Just '9'+    10 -> Just 'a'+    11 -> Just 'b'+    12 -> Just 'c'+    13 -> Just 'd'+    14 -> Just 'e'+    15 -> Just 'f'+    _ -> Nothing
+ tests/Main.hs view
@@ -0,0 +1,4 @@+import qualified Test.DocTest as Doctest++main :: IO ()+main = Doctest.doctest ["library"]