module Language.JSON.Parser

import Language.JSON.Data
import Text.Parser
import Text.Token
import Data.List

import public Language.JSON.Tokens

%default total

private
punct : Punctuation -> Grammar state JSONToken True ()
punct p = match $ JTPunct p

private
rawString : Grammar state JSONToken True String
rawString = do mstr <- match JTString
               the (Grammar _ _ False _) $
                   case mstr of
                        Just str => pure str
                        Nothing => fail "invalid string"

mutual
  private
  json : Grammar state JSONToken True JSON
  json = object
     <|> array
     <|> string
     <|> boolean
     <|> number
     <|> null

  private
  object : Grammar state JSONToken True JSON
  object = do punct $ Curly Open
              commit
              props <- properties
              punct $ Curly Close
              pure $ JObject props
    where
      properties : Grammar state JSONToken False (List (String, JSON))
      properties = sepBy (punct Comma) $
                         do key <- rawString
                            punct Colon
                            value <- json
                            pure (key, value)

  private
  array : Grammar state JSONToken True JSON
  array = do punct (Square Open)
             commit
             vals <- values
             punct (Square Close)
             pure (JArray vals)
    where
      values : Grammar state JSONToken False (List JSON)
      values = sepBy (punct Comma) json

  private
  string : Grammar state JSONToken True JSON
  string = map JString rawString

  private
  boolean : Grammar state JSONToken True JSON
  boolean = map JBoolean $ match JTBoolean

  private
  number : Grammar state JSONToken True JSON
  number = map JNumber $ match JTNumber

  private
  null : Grammar state JSONToken True JSON
  null = map (const JNull) $ match JTNull

export
parseJSON : List (WithBounds JSONToken) -> Maybe JSON
parseJSON toks = case parse json $ filter (not . ignored) toks of
                      Right (j, []) => Just j
                      _ => Nothing
