# LANGUAGE NoImplicitPrelude #
# LANGUAGE ScopedTypeVariables #
{-# LANGUAGE OverloadedStrings #-}
# LANGUAGE RebindableSyntax #

module Course.JsonParser where

import Course.Core
import Course.Parser
import Course.MoreParser
import Course.JsonValue
import Course.Functor
import Course.Applicative
import Course.Monad
import Course.List
import Course.Optional

-- $setup
-- >>> :set -XOverloadedStrings

-- A special character is one of the following:
  * \b   Backspace ( ascii code 08 )
-- * \f  Form feed (ascii code 0C)
  *    line
-- * \r  Carriage return
-- * \t  Tab
  *    tab
-- * \'  Apostrophe or single quote (only valid in single quoted json strings)
  * \ "   Double quote ( only valid in double quoted   )
  * \\   Backslash character
data SpecialCharacter =
  BackSpace
  | FormFeed
  | NewLine
  | CarriageReturn
  | Tab
  | VerticalTab
  | SingleQuote
  | DoubleQuote
  | Backslash
  deriving (Eq, Ord, Show)

-- NOTE: This is not inverse to @toSpecialCharacter@.
fromSpecialCharacter ::
  SpecialCharacter
  -> Char
fromSpecialCharacter BackSpace =
  chr 0x08
fromSpecialCharacter FormFeed =
  chr 0x0C
fromSpecialCharacter NewLine =
  '\n'
fromSpecialCharacter CarriageReturn =
  '\r'
fromSpecialCharacter Tab =
  '\t'
fromSpecialCharacter VerticalTab =
  '\v'
fromSpecialCharacter SingleQuote =
  '\''
fromSpecialCharacter DoubleQuote =
  '"'
fromSpecialCharacter Backslash =
  '\\'

-- NOTE: This is not inverse to @fromSpecialCharacter@.
toSpecialCharacter ::
  Char
  -> Optional SpecialCharacter
toSpecialCharacter c =
  let table = ('b', BackSpace) :.
              ('f', FormFeed) :.
              ('n', NewLine) :.
              ('r', CarriageReturn) :.
              ('t', Tab) :.
              ('v', VerticalTab) :.
              ('\'', SingleQuote) :.
              ('"' , DoubleQuote) :.
              ('\\', Backslash) :.
              Nil
  in snd <$> find ((==) c . fst) table
  
  | Parse a JSON string . Handle double - quotes , special characters , hexadecimal characters . See  for the full list of control characters in JSON .
--
  /Tip:/ Use ` hex ` , ` fromSpecialCharacter ` , ` between ` , ` is ` , ` charTok ` , ` toSpecialCharacter ` .
--
-- >>> parse jsonString "\" abc\""
  Result > < " abc "
--
-- >>> parse jsonString "\"abc\"def"
  Result > def < " abc "
--
-- >>> parse jsonString "\"\\babc\"def"
-- Result >def< "\babc"
--
-- >>> parse jsonString "\"\\u00abc\"def"
-- Result >def< "\171c"
--
  > > > parse   \"\\u00ffabc\"def "
  Result > def < " \255abc "
--
-- >>> parse jsonString "\"\\u00faabc\"def"
-- Result >def< "\250abc"
--
  > > > isErrorResult ( parse jsonString " abc " )
-- True
--
-- >>> isErrorResult (parse jsonString "\"\\abc\"def")
-- True
jsonString ::
  Parser Chars
jsonString =
  error "todo: Course.JsonParser#jsonString"

-- | Parse a JSON rational.
--
  /Tip:/ Use @readFloats@.
--
  > > > parse  " 234 "
  Result > < 234 % 1
--
  > > > parse  " -234 "
-- Result >< (-234) % 1
--
  > > > parse  " 123.45 "
  Result > < 2469 % 20
--
  > > > parse  " -123 "
-- Result >< (-123) % 1
--
  > > > parse  " -123.45 "
  Result > < ( -2469 ) % 20
--
-- >>> isErrorResult (parse jsonNumber "-")
-- True
--
  > > > isErrorResult ( parse jsonNumber " abc " )
-- True
jsonNumber ::
  Parser Rational
jsonNumber =
  error "todo: Course.JsonParser#jsonNumber"

-- | Parse a JSON true literal.
--
-- /Tip:/ Use `stringTok`.
--
-- >>> parse jsonTrue "true"
-- Result >< "true"
--
-- >>> isErrorResult (parse jsonTrue "TRUE")
-- True
jsonTrue ::
  Parser Chars
jsonTrue =
  error "todo: Course.JsonParser#jsonTrue"

-- | Parse a JSON false literal.
--
-- /Tip:/ Use `stringTok`.
--
-- >>> parse jsonFalse "false"
-- Result >< "false"
--
-- >>> isErrorResult (parse jsonFalse "FALSE")
-- True
jsonFalse ::
  Parser Chars
jsonFalse =
  error "todo: Course.JsonParser#jsonFalse"

-- | Parse a JSON null literal.
--
-- /Tip:/ Use `stringTok`.
--
-- >>> parse jsonNull "null"
-- Result >< "null"
--
-- >>> isErrorResult (parse jsonNull "NULL")
-- True
jsonNull ::
  Parser Chars
jsonNull =
  error "todo: Course.JsonParser#jsonNull"

-- | Parse a JSON array.
--
  /Tip:/ Use ` betweenSepbyComma ` and ` jsonValue ` .
--
  > > > parse  " [ ] "
-- Result >< []
--
  > > > parse  " [ true ] "
-- Result >< [JsonTrue]
--
  > > > parse  " [ true , \"abc\ " ] "
  Result > < [ JsonTrue ,  " abc " ]
--
  > > > parse  " [ true , \"abc\ " , [ ] ] "
  Result > < [ JsonTrue ,  " abc",JsonArray [ ] ]
--
  > > > parse  " [ true , \"abc\ " , [ false ] ] "
  Result > < [ JsonTrue ,  " abc",JsonArray [ JsonFalse ] ]
jsonArray ::
  Parser (List JsonValue)
jsonArray =
  error "todo: Course.JsonParser#jsonArray"

-- | Parse a JSON object.
--
-- /Tip:/ Use `jsonString`, `charTok`, `betweenSepbyComma` and `jsonValue`.
--
  > > > parse jsonObject " { } "
-- Result >< []
--
  > > > parse jsonObject " { \"key1\ " : true } "
-- Result >< [("key1",JsonTrue)]
--
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : false } "
  Result > < [ ( " key1",JsonTrue),("key2",JsonFalse ) ]
--
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : false } xyz "
  Result > xyz < [ ( " key1",JsonTrue),("key2",JsonFalse ) ]
jsonObject ::
  Parser Assoc
jsonObject =
  error "todo: Course.JsonParser#jsonObject"

-- | Parse a JSON value.
--
-- /Tip:/ Use `spaces`, `jsonNull`, `jsonTrue`, `jsonFalse`, `jsonArray`, `jsonString`, `jsonObject` and `jsonNumber`.
--
-- >>> parse jsonValue "true"
-- Result >< JsonTrue
--
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : [ 7 , false ] } "
  Result > < [ ( " key1",JsonTrue),("key2",JsonArray [ JsonRational ( 7 % 1),JsonFalse ] ) ]
--
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : [ 7 , false ] , \"key3\ " : { \"key4\ " : null } } "
  Result > < [ ( " key1",JsonTrue),("key2",JsonArray [ JsonRational ( 7 % 1),JsonFalse]),("key3",JsonObject [ ( " key4",JsonNull ) ] ) ]
jsonValue ::
  Parser JsonValue
jsonValue =
   error "todo: Course.JsonParser#jsonValue"

-- | Read a file into a JSON value.
--
  /Tip:/ Use @System . IO#readFile@ and ` jsonValue ` .
readJsonValue ::
  FilePath
  -> IO (ParseResult JsonValue)
readJsonValue =
  error "todo: Course.JsonParser#readJsonValue"

# LANGUAGE OverloadedStrings #
 $setup
 >>> :set -XOverloadedStrings
 A special character is one of the following:
 * \f  Form feed (ascii code 0C)
 * \r  Carriage return
 * \t  Tab
 * \'  Apostrophe or single quote (only valid in single quoted json strings)
 NOTE: This is not inverse to @toSpecialCharacter@.
 NOTE: This is not inverse to @fromSpecialCharacter@.


 >>> parse jsonString "\" abc\""

 >>> parse jsonString "\"abc\"def"

 >>> parse jsonString "\"\\babc\"def"
 Result >def< "\babc"

 >>> parse jsonString "\"\\u00abc\"def"
 Result >def< "\171c"


 >>> parse jsonString "\"\\u00faabc\"def"
 Result >def< "\250abc"

 True

 >>> isErrorResult (parse jsonString "\"\\abc\"def")
 True
 | Parse a JSON rational.



 Result >< (-234) % 1


 Result >< (-123) % 1


 >>> isErrorResult (parse jsonNumber "-")
 True

 True
 | Parse a JSON true literal.

 /Tip:/ Use `stringTok`.

 >>> parse jsonTrue "true"
 Result >< "true"

 >>> isErrorResult (parse jsonTrue "TRUE")
 True
 | Parse a JSON false literal.

 /Tip:/ Use `stringTok`.

 >>> parse jsonFalse "false"
 Result >< "false"

 >>> isErrorResult (parse jsonFalse "FALSE")
 True
 | Parse a JSON null literal.

 /Tip:/ Use `stringTok`.

 >>> parse jsonNull "null"
 Result >< "null"

 >>> isErrorResult (parse jsonNull "NULL")
 True
 | Parse a JSON array.


 Result >< []

 Result >< [JsonTrue]



 | Parse a JSON object.

 /Tip:/ Use `jsonString`, `charTok`, `betweenSepbyComma` and `jsonValue`.

 Result >< []

 Result >< [("key1",JsonTrue)]


 | Parse a JSON value.

 /Tip:/ Use `spaces`, `jsonNull`, `jsonTrue`, `jsonFalse`, `jsonArray`, `jsonString`, `jsonObject` and `jsonNumber`.

 >>> parse jsonValue "true"
 Result >< JsonTrue


 | Read a file into a JSON value.

# LANGUAGE NoImplicitPrelude #
# LANGUAGE ScopedTypeVariables #
# LANGUAGE RebindableSyntax #

module Course.JsonParser where

import Course.Core
import Course.Parser
import Course.MoreParser
import Course.JsonValue
import Course.Functor
import Course.Applicative
import Course.Monad
import Course.List
import Course.Optional


  * \b   Backspace ( ascii code 08 )
  *    line
  *    tab
  * \ "   Double quote ( only valid in double quoted   )
  * \\   Backslash character
data SpecialCharacter =
  BackSpace
  | FormFeed
  | NewLine
  | CarriageReturn
  | Tab
  | VerticalTab
  | SingleQuote
  | DoubleQuote
  | Backslash
  deriving (Eq, Ord, Show)

fromSpecialCharacter ::
  SpecialCharacter
  -> Char
fromSpecialCharacter BackSpace =
  chr 0x08
fromSpecialCharacter FormFeed =
  chr 0x0C
fromSpecialCharacter NewLine =
  '\n'
fromSpecialCharacter CarriageReturn =
  '\r'
fromSpecialCharacter Tab =
  '\t'
fromSpecialCharacter VerticalTab =
  '\v'
fromSpecialCharacter SingleQuote =
  '\''
fromSpecialCharacter DoubleQuote =
  '"'
fromSpecialCharacter Backslash =
  '\\'

toSpecialCharacter ::
  Char
  -> Optional SpecialCharacter
toSpecialCharacter c =
  let table = ('b', BackSpace) :.
              ('f', FormFeed) :.
              ('n', NewLine) :.
              ('r', CarriageReturn) :.
              ('t', Tab) :.
              ('v', VerticalTab) :.
              ('\'', SingleQuote) :.
              ('"' , DoubleQuote) :.
              ('\\', Backslash) :.
              Nil
  in snd <$> find ((==) c . fst) table
  
  | Parse a JSON string . Handle double - quotes , special characters , hexadecimal characters . See  for the full list of control characters in JSON .
  /Tip:/ Use ` hex ` , ` fromSpecialCharacter ` , ` between ` , ` is ` , ` charTok ` , ` toSpecialCharacter ` .
  Result > < " abc "
  Result > def < " abc "
  > > > parse   \"\\u00ffabc\"def "
  Result > def < " \255abc "
  > > > isErrorResult ( parse jsonString " abc " )
jsonString ::
  Parser Chars
jsonString =
  error "todo: Course.JsonParser#jsonString"

  /Tip:/ Use @readFloats@.
  > > > parse  " 234 "
  Result > < 234 % 1
  > > > parse  " -234 "
  > > > parse  " 123.45 "
  Result > < 2469 % 20
  > > > parse  " -123 "
  > > > parse  " -123.45 "
  Result > < ( -2469 ) % 20
  > > > isErrorResult ( parse jsonNumber " abc " )
jsonNumber ::
  Parser Rational
jsonNumber =
  error "todo: Course.JsonParser#jsonNumber"

jsonTrue ::
  Parser Chars
jsonTrue =
  error "todo: Course.JsonParser#jsonTrue"

jsonFalse ::
  Parser Chars
jsonFalse =
  error "todo: Course.JsonParser#jsonFalse"

jsonNull ::
  Parser Chars
jsonNull =
  error "todo: Course.JsonParser#jsonNull"

  /Tip:/ Use ` betweenSepbyComma ` and ` jsonValue ` .
  > > > parse  " [ ] "
  > > > parse  " [ true ] "
  > > > parse  " [ true , \"abc\ " ] "
  Result > < [ JsonTrue ,  " abc " ]
  > > > parse  " [ true , \"abc\ " , [ ] ] "
  Result > < [ JsonTrue ,  " abc",JsonArray [ ] ]
  > > > parse  " [ true , \"abc\ " , [ false ] ] "
  Result > < [ JsonTrue ,  " abc",JsonArray [ JsonFalse ] ]
jsonArray ::
  Parser (List JsonValue)
jsonArray =
  error "todo: Course.JsonParser#jsonArray"

  > > > parse jsonObject " { } "
  > > > parse jsonObject " { \"key1\ " : true } "
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : false } "
  Result > < [ ( " key1",JsonTrue),("key2",JsonFalse ) ]
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : false } xyz "
  Result > xyz < [ ( " key1",JsonTrue),("key2",JsonFalse ) ]
jsonObject ::
  Parser Assoc
jsonObject =
  error "todo: Course.JsonParser#jsonObject"

  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : [ 7 , false ] } "
  Result > < [ ( " key1",JsonTrue),("key2",JsonArray [ JsonRational ( 7 % 1),JsonFalse ] ) ]
  > > > parse jsonObject " { \"key1\ " : true , \"key2\ " : [ 7 , false ] , \"key3\ " : { \"key4\ " : null } } "
  Result > < [ ( " key1",JsonTrue),("key2",JsonArray [ JsonRational ( 7 % 1),JsonFalse]),("key3",JsonObject [ ( " key4",JsonNull ) ] ) ]
jsonValue ::
  Parser JsonValue
jsonValue =
   error "todo: Course.JsonParser#jsonValue"

  /Tip:/ Use @System . IO#readFile@ and ` jsonValue ` .
readJsonValue ::
  FilePath
  -> IO (ParseResult JsonValue)
readJsonValue =
  error "todo: Course.JsonParser#readJsonValue"
