{-# LANGUAGE DeriveGeneric #-}
module Colorless.Ast
  ( Ast(..)
  , ToAst(..)
  , Ref(..)
  , If(..)
  , Get(..)
  , Define(..)
  , Lambda(..)
  , List(..)
  , Tuple(..)
  , Do(..)
  , FnCall(..)
  , Enumeral(..)
  , Struct(..)
  , Wrap(..)
  , StructCall(..)
  , WrapCall(..)
  , EnumerationCall(..)
  , HollowCall(..)
  , Const(..)
  ) where

import qualified Data.HashMap.Lazy as HML
import qualified Data.Vector as V
import qualified Data.Map as Map
import Control.Monad (mzero)
import Control.Applicative ((<|>))
import Data.Text (Text)
import Data.Aeson
import Data.Map (Map)
import Data.Int
import Data.Word
import GHC.Generics (Generic)

import Colorless.Types

data Ast
  = Ast'Ref Ref
  | Ast'If If
  | Ast'Get Get
  | Ast'Define Define
  | Ast'Lambda Lambda
  | Ast'List List
  | Ast'Tuple Tuple
  | Ast'Do Do
  | Ast'FnCall FnCall
  | Ast'WrapCall WrapCall
  | Ast'StructCall StructCall
  | Ast'EnumerationCall EnumerationCall
  | Ast'HollowCall HollowCall
  | Ast'Enumeral Enumeral
  | Ast'Struct Struct
  | Ast'Wrap Wrap
  | Ast'Const Const
  deriving (Show, Eq)

class ToAst a where
  toAst :: a -> Ast

instance ToAst () where
  toAst () = Ast'Const Const'Null

instance ToAst Bool where
  toAst b = Ast'Const (Const'Bool b)

instance ToAst Text where
  toAst s = Ast'Const (Const'String s)

instance ToAst Int8 where
  toAst = num

instance ToAst Int16 where
  toAst = num

instance ToAst Int32 where
  toAst = num

instance ToAst Int64 where
  toAst = num

instance ToAst Word8 where
  toAst = num

instance ToAst Word16 where
  toAst = num

instance ToAst Word32 where
  toAst = num

instance ToAst Word64 where
  toAst = num

instance ToAst Float where
  toAst = num

instance ToAst Double where
  toAst = num

num :: (Num a, ToJSON a) => a -> Ast
num n = case toJSON n of
  Number a -> Ast'Const (Const'Number a)
  _ -> error "should never reach here"

instance ToAst a => ToAst [a] where
  toAst xs = Ast'List $ List $ map toAst xs

instance ToAst a => ToAst (Maybe a) where
  toAst Nothing = Ast'Const Const'Null
  toAst (Just x) = toAst x

instance (ToAst a, ToAst b) => ToAst (Either a b) where
  toAst (Left a) = Ast'Enumeral $ Enumeral "Left" $ Just $ Map.fromList [("left", toAst a)]
  toAst (Right a) = Ast'Enumeral $ Enumeral "Right" $ Just $ Map.fromList [("right", toAst a)]

--

instance (ToAst t1, ToAst t2) => ToAst (t1, t2) where
  toAst (t1, t2) = Ast'Tuple $ Tuple [toAst t1, toAst t2]

instance (ToAst t1, ToAst t2, ToAst t3) => ToAst (t1, t2, t3) where
  toAst (t1, t2, t3) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4) => ToAst (t1, t2, t3, t4) where
  toAst (t1, t2, t3, t4) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5) => ToAst (t1, t2, t3, t4, t5) where
  toAst (t1, t2, t3, t4, t5) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6) => ToAst (t1, t2, t3, t4, t5, t6) where
  toAst (t1, t2, t3, t4, t5, t6) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7) => ToAst (t1, t2, t3, t4, t5, t6, t7) where
  toAst (t1, t2, t3, t4, t5, t6, t7) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26, ToAst t27) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26, toAst t27]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26, ToAst t27, ToAst t28) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26, toAst t27, toAst t28]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26, ToAst t27, ToAst t28, ToAst t29) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26, toAst t27, toAst t28, toAst t29]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26, ToAst t27, ToAst t28, ToAst t29, ToAst t30) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26, toAst t27, toAst t28, toAst t29, toAst t30]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26, ToAst t27, ToAst t28, ToAst t29, ToAst t30, ToAst t31) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26, toAst t27, toAst t28, toAst t29, toAst t30, toAst t31]

instance (ToAst t1, ToAst t2, ToAst t3, ToAst t4, ToAst t5, ToAst t6, ToAst t7, ToAst t8, ToAst t9, ToAst t10, ToAst t11, ToAst t12, ToAst t13, ToAst t14, ToAst t15, ToAst t16, ToAst t17, ToAst t18, ToAst t19, ToAst t20, ToAst t21, ToAst t22, ToAst t23, ToAst t24, ToAst t25, ToAst t26, ToAst t27, ToAst t28, ToAst t29, ToAst t30, ToAst t31, ToAst t32) => ToAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31, t32) where
  toAst (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31, t32) = Ast'Tuple $ Tuple [toAst t1, toAst t2, toAst t3, toAst t4, toAst t5, toAst t6, toAst t7, toAst t8, toAst t9, toAst t10, toAst t11, toAst t12, toAst t13, toAst t14, toAst t15, toAst t16, toAst t17, toAst t18, toAst t19, toAst t20, toAst t21, toAst t22, toAst t23, toAst t24, toAst t25, toAst t26, toAst t27, toAst t28, toAst t29, toAst t30, toAst t31, toAst t32]

{-
var tuples = [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32];
var toAstTuple = n => {
  if (n < 2) {
    return '';
  }

  var l = ['instance (ToAst t1'];
  for (var i = 1; i < n; i++) {
    l = l.concat([', ToAst t', i + 1]);
  }
  l = l.concat([') => ToAst (t1']);
  for (var i = 1; i < n; i++) {
    l = l.concat([', t', i + 1]);
  }
  l = l.concat([') where\n']);

  l = l.concat(['  toAst (t1']);
  for (var i = 1; i < n; i++) {
    l = l.concat([', t', i + 1]);
  }
  l = l.concat([') = Ast\'Tuple $ Tuple [toAst t1']);
  for (var i = 1; i < n; i++) {
    l = l.concat([', toAst t', i + 1]);
  }
  l = l.concat([']\n\n']);

  return l.join('');
};
-}

--

instance FromJSON Ast where
  parseJSON v
    =   (Ast'Ref <$> parseJSON v)
    <|> (Ast'If <$> parseJSON v)
    <|> (Ast'Get <$> parseJSON v)
    <|> (Ast'Define <$> parseJSON v)
    <|> (Ast'Lambda <$> parseJSON v)
    <|> (Ast'List <$> parseJSON v)
    <|> (Ast'Tuple <$> parseJSON v)
    <|> (Ast'Do <$> parseJSON v)
    <|> (Ast'FnCall <$> parseJSON v)
    <|> (Ast'EnumerationCall <$> parseJSON v)
    <|> (Ast'WrapCall <$> parseJSON v)
    <|> (Ast'StructCall <$> parseJSON v)
    <|> (Ast'HollowCall <$> parseJSON v)
    <|> (Ast'Enumeral <$> parseJSON v)
    <|> (Ast'Struct <$> parseJSON v)
    <|> (Ast'Const <$> parseJSON v)

instance ToJSON Ast where
  toJSON = \case
    Ast'Ref a -> toJSON a
    Ast'If a -> toJSON a
    Ast'Get a -> toJSON a
    Ast'Define a -> toJSON a
    Ast'Lambda a -> toJSON a
    Ast'List a -> toJSON a
    Ast'Tuple a -> toJSON a
    Ast'Do a -> toJSON a
    Ast'FnCall a -> toJSON a
    Ast'EnumerationCall a -> toJSON a
    Ast'WrapCall a -> toJSON a
    Ast'StructCall a -> toJSON a
    Ast'HollowCall a -> toJSON a
    Ast'Enumeral a -> toJSON a
    Ast'Struct a -> toJSON a
    Ast'Wrap a -> toJSON a
    Ast'Const a -> toJSON a

data Ref = Ref
  { symbol :: Symbol
  } deriving (Show, Eq)

instance FromJSON Ref where
  parseJSON (Object o) = Ref <$> o .: "@"
  parseJSON _ = mzero

instance ToJSON Ref where
  toJSON Ref{symbol} = object [ "@" .= symbol ]

data If = If
  { cond :: Ast
  , true :: Ast
  , false :: Ast
  } deriving (Show, Eq)

instance FromJSON If where
  parseJSON (Array arr) = case V.toList arr of
    ["if", cond, true, false] -> If <$> parseJSON cond <*> parseJSON true <*> parseJSON false
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON If where
  toJSON If{cond,true,false} = toJSON ["if", toJSON cond, toJSON true, toJSON false]

data Get = Get
  { path :: [Text]
  , val :: Ast
  } deriving (Show, Eq)

instance FromJSON Get where
  parseJSON (Array arr) = case V.toList arr of
    ["get", path, val] -> Get <$> parseJSON path <*> parseJSON val
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON Get where
  toJSON Get{path,val} = toJSON ["get", toJSON path, toJSON val]

data Define = Define
  { var :: Symbol
  , expr :: Ast
  } deriving (Show, Eq)

instance FromJSON Define where
  parseJSON (Array arr) = case V.toList arr of
    ["def", var, expr] -> Define <$> parseJSON var <*> parseJSON expr
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON Define where
  toJSON Define{var,expr} = toJSON ["def", toJSON var, toJSON expr]

data Lambda = Lambda
  { args :: [(Symbol, Type)]
  , expr :: Ast
  } deriving (Show, Eq)

instance FromJSON Lambda where
  parseJSON (Array arr) = case V.toList arr of
    ["fn", Array params, expr] -> Lambda <$> mapM parseParam (V.toList params) <*> parseJSON expr
    _ -> mzero
    where
      parseParam = \case
        Object o -> case HML.toList o of
          [(key,val)] -> (Symbol key,) <$> parseJSON val
          _ -> mzero
        _ -> mzero
  parseJSON _ = mzero

instance ToJSON Lambda where
  toJSON Lambda{args,expr} = toJSON ["fn", toJSON $ map (Map.fromList . (:[])) args, toJSON expr]

data List = List
  { list :: [Ast]
  } deriving (Show, Eq)

instance FromJSON List where
  parseJSON (Array arr) = case V.toList arr of
    ("list":(Array xs):[]) -> List <$> mapM parseJSON (V.toList xs)
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON List where
  toJSON List{list} = toJSON $ "list" : [toJSON list]

data Tuple = Tuple
  { tuple :: [Ast]
  } deriving (Show, Eq)

instance FromJSON Tuple where
  parseJSON (Array arr) = case V.toList arr of
    ("tuple":xs) -> Tuple <$> mapM parseJSON xs
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON Tuple where
  toJSON Tuple{tuple} = toJSON $ "tuple" : map toJSON tuple

data Do = Do
  { vals :: [Ast]
  } deriving (Show, Eq)

instance FromJSON Do where
  parseJSON (Array arr) = case V.toList arr of
    "do":xs -> Do <$> mapM parseJSON xs
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON Do where
  toJSON Do{vals} = toJSON $ "do" : map toJSON vals

data FnCall = FnCall
  { fn :: Ast
  , args :: [Ast]
  } deriving (Show, Eq)

instance FromJSON FnCall where
  parseJSON (Array arr) = case V.toList arr of
    ((String x):xs) -> FnCall <$> pure (Ast'Ref $ Ref $ Symbol x) <*> mapM parseJSON xs
    (x:xs) -> FnCall <$> parseJSON x <*> mapM parseJSON xs
    _ -> mzero
  parseJSON _ = mzero

instance ToJSON FnCall where
  toJSON (FnCall (Ast'Ref (Ref sym)) args) = toJSON $ toJSON sym : map toJSON args
  toJSON (FnCall fn args) = toJSON $ fn : args

data EnumerationCall = EnumerationCall
  { n :: TypeName
  , e :: Ast
  } deriving (Show, Eq, Generic)

instance FromJSON EnumerationCall
instance ToJSON EnumerationCall

data WrapCall = WrapCall
  { n :: TypeName
  , w :: Ast
  } deriving (Show, Eq, Generic)

instance FromJSON WrapCall
instance ToJSON WrapCall

data StructCall = StructCall
  { n :: TypeName
  , m :: Ast
  } deriving (Show, Eq, Generic)

instance FromJSON StructCall
instance ToJSON StructCall

data HollowCall = HollowCall
  { n :: TypeName
  } deriving (Show, Eq, Generic)

instance FromJSON HollowCall
instance ToJSON HollowCall

data Enumeral = Enumeral
  { tag :: EnumeralName
  , m :: Maybe (Map MemberName Ast)
  } deriving (Show, Eq, Generic)

instance FromJSON Enumeral where
  parseJSON (Object o) = do
    tag <- o .: "tag"
    let tagless = HML.delete "tag" o
    if HML.size o == 1
      then pure $ Enumeral tag Nothing
      else Enumeral tag <$> (Just <$> parseJSON (Object tagless))
  parseJSON _ = mzero

instance ToJSON Enumeral where
  toJSON Enumeral{tag, m} = case m of
    Nothing -> object [ "tag" .= tag ]
    Just m' -> toJSON $ Map.fromList $ ("tag", toJSON tag) : Map.toList (toJSON <$> m')

data Struct = Struct
  { m :: Map MemberName Ast
  } deriving (Show, Eq, Generic)

instance FromJSON Struct where
  parseJSON v = Struct <$> parseJSON v

instance ToJSON Struct where
  toJSON (Struct m) = toJSON m

data Wrap = Wrap
  { w :: Ast
  } deriving (Show, Eq, Generic)

-- Wrap does not have a FromJSON instance
-- 'Wrapped' values will be coerced as needed

instance ToJSON Wrap where
  toJSON (Wrap w) = toJSON w