termCodec :: (ToJSON n, FromJSON n) => Codec (Term n)
termCodec = Codec enc dec
  where
    enc t = case t of
      TModule d b i -> ob [ moduleDef .= d, body .= b, inf i ]
      TList ts ty i -> ob [ list' .= ts, type' .= ty, inf i ]
      TDef d _i -> toJSON d
      -- TNative intentionally not marshallable
      TNative n _fn tys _exs d tl i ->
        ob [ natName .= n, natFun .= Null {- TODO fn -}
           , natFunTypes .= tys, natExamples .= Null {- TODO exs -},
             natDocs .= d, natTopLevel .= tl, inf i ]
      TConst d m c met i ->
        ob [ constArg .= d, modName .= m, constVal .= c, meta .= met, inf i ]
      TApp a _i -> toJSON a
      TVar n i -> ob [ var .= n, inf i ]
      TBinding bs b c i -> ob [pairs .= bs, body .= b, type' .= c, inf i]
      TObject o _i -> toJSON o
      TLiteral l i -> ob [literal .= l, inf i]
      TLam tlam _i -> toJSON tlam

      TGuard k i -> ob [guard' .= k, inf i]
      TUse u _i -> toJSON u
      TStep s tmeta i -> ob [body .= s, meta .= tmeta, inf i]
      TSchema sn smod smeta sfs i ->
        ob [ schemaName .= sn, modName .= smod, meta .= smeta
           , fields .= sfs, inf i ]
      TTable tn tmod th tty tmeta i ->
        ob [ tblName .= tn, modName .= tmod, hash' .= th, type' .= tty
           , meta .= tmeta, inf i ]
      TDynamic r m i ->
        ob [ dynRef .= r, dynMem .= m, inf i ]
      TModRef mr _i -> toJSON mr

    dec decval =
      let wo n f = withObject n f decval
          parseWithInfo f = uncurry f . (id &&& getInfo) <$> parseJSON decval
      in
        wo "Module" (\o -> TModule <$> o .: moduleDef <*> o .: body <*> inf' o)
        <|> wo "List"
            (\o -> TList <$> o .: list' <*> o .: type' <*> inf' o)
        <|> parseWithInfo TDef
      -- TNative intentionally not marshallable
        <|> wo "Const"
            (\o -> TConst <$> o .: constArg <*> o .: modName
              <*> o .: constVal <*> o .: meta <*> inf' o )
        <|> parseWithInfo TApp
        <|> wo "Var" (\o -> TVar <$>  o .: var <*> inf' o )
        <|> wo "Binding"
            (\o -> TBinding <$> o .: pairs <*> o .: body
              <*> o .: type' <*> inf' o)
        <|> parseWithInfo TObject
        <|> wo "Literal" (\o -> TLiteral <$> o .: literal <*> inf' o)
        <|> wo "Guard" (\o -> TGuard <$> o .: guard' <*> inf' o)
        <|> parseWithInfo TUse
        <|> parseWithInfo TLam
        <|> wo "Step"
            (\o -> TStep <$> o .: body <*> o .: meta <*> inf' o)
       --  parseWithInfo TStep
        <|> wo "Schema"
            (\o -> TSchema <$>  o .: schemaName <*> o .: modName
              <*> o .: meta <*> o .: fields <*> inf' o )
        <|> wo "Table"
            (\o -> TTable <$>  o .: tblName <*> o .: modName
              <*> o .: hash' <*> o .: type'
              <*> o .: meta <*> inf' o )
        <|> wo "Dynamic"
            (\o -> TDynamic <$> o .: dynRef <*> o .: dynMem <*> inf' o)

        <|> parseWithInfo TModRef

    ob = object
    moduleDef = "module"
    body = "body"
    meta = "meta"
    inf i = "i" .= i
    inf' o = o .: "i"
    list' = "list"
    type' = "type"
    natName = "name"
    natFun = "fun"
    natFunTypes = "types"
    natExamples = "examples"
    natDocs = "docs"
    natTopLevel = "tl"
    constArg = "arg"
    modName = "modname"
    constVal = "val"
    var = "var"
    pairs = "pairs"
    literal = "lit"
    guard' = "guard"
    schemaName = "name"
    fields = "fields"
    tblName = "name"
    hash' = "hash"
    dynRef = "dref"
    dynMem = "dmem"
termCodec :: (ToJSON n, FromJSON n) => Codec (Term n)
termCodec = Codec enc dec
  where
    enc t = case t of
      TModule d b i -> ob [ moduleDef .= d, body .= b, inf i ]
      TList ts ty i -> ob [ list' .= ts, type' .= ty, inf i ]
      TDef d _i -> toJSON d
      -- TNative intentionally not marshallable
      TNative n _fn tys _exs d tl i ->
        ob [ natName .= n, natFun .= Null {- TODO fn -}
           , natFunTypes .= tys, natExamples .= Null {- TODO exs -},
             natDocs .= d, natTopLevel .= tl, inf i ]
      TConst d m c met i ->
        ob [ constArg .= d, modName .= m, constVal .= c, meta .= met, inf i ]
      TApp a _i -> toJSON a
      TVar n i -> ob [ var .= n, inf i ]
      TBinding bs b c i -> ob [pairs .= bs, body .= b, type' .= c, inf i]
      TObject o _i -> toJSON o
      TLiteral l i -> ob [literal .= l, inf i]
      TLam tlam _i -> toJSON tlam

      TGuard k i -> ob [guard' .= k, inf i]
      TUse u _i -> toJSON u
      TStep s tmeta i -> ob [body .= s, meta .= tmeta, inf i]
      TSchema sn smod smeta sfs i ->
        ob [ schemaName .= sn, modName .= smod, meta .= smeta
           , fields .= sfs, inf i ]
      TTable tn tmod th tty tmeta i ->
        ob [ tblName .= tn, modName .= tmod, hash' .= th, type' .= tty
           , meta .= tmeta, inf i ]
      TDynamic r m i ->
        ob [ dynRef .= r, dynMem .= m, inf i ]
      TModRef mr _i -> toJSON mr

    dec decval =
      let wo n f = withObject n f decval
          parseWithInfo f = uncurry f . (id &&& getInfo) <$> parseJSON decval
      in
        wo "Module" (\o -> TModule <$> o .: moduleDef <*> o .: body <*> inf' o)
        <|> wo "List"
            (\o -> TList <$> o .: list' <*> o .: type' <*> inf' o)
        <|> parseWithInfo TDef
      -- TNative intentionally not marshallable
        <|> wo "Const"
            (\o -> TConst <$> o .: constArg <*> o .: modName
              <*> o .: constVal <*> o .: meta <*> inf' o )
        <|> parseWithInfo TApp
        <|> wo "Var" (\o -> TVar <$>  o .: var <*> inf' o )
        <|> wo "Binding"
            (\o -> TBinding <$> o .: pairs <*> o .: body
              <*> o .: type' <*> inf' o)
        <|> parseWithInfo TObject
        <|> wo "Literal" (\o -> TLiteral <$> o .: literal <*> inf' o)
        <|> wo "Guard" (\o -> TGuard <$> o .: guard' <*> inf' o)
        <|> parseWithInfo TUse
        <|> parseWithInfo TLam
        <|> wo "Step"
            (\o -> TStep <$> o .: body <*> o .: meta <*> inf' o)
       --  parseWithInfo TStep
        <|> wo "Schema"
            (\o -> TSchema <$>  o .: schemaName <*> o .: modName
              <*> o .: meta <*> o .: fields <*> inf' o )
        <|> wo "Table"
            (\o -> TTable <$>  o .: tblName <*> o .: modName
              <*> o .: hash' <*> o .: type'
              <*> o .: meta <*> inf' o )
        <|> wo "Dynamic"
            (\o -> TDynamic <$> o .: dynRef <*> o .: dynMem <*> inf' o)

        <|> parseWithInfo TModRef

    ob = object
    moduleDef = "module"
    body = "body"
    meta = "meta"
    inf i = "i" .= i
    inf' o = o .: "i"
    list' = "list"
    type' = "type"
    natName = "name"
    natFun = "fun"
    natFunTypes = "types"
    natExamples = "examples"
    natDocs = "docs"
    natTopLevel = "tl"
    constArg = "arg"
    modName = "modname"
    constVal = "val"
    var = "var"
    pairs = "pairs"
    literal = "lit"
    guard' = "guard"
    schemaName = "name"
    fields = "fields"
    tblName = "name"
    hash' = "hash"
    dynRef = "dref"
    dynMem = "dmem"
termCodec = Codec enc dec
  where
    enc t = case t of
      TModule d b i -> ob [ moduleDef .= d, body .= b, inf i ]
      TList ts ty i -> ob [ list' .= ts, type' .= ty, inf i ]
      TDef d _i -> toJSON d
      -- TNative intentionally not marshallable
      TNative n _fn tys _exs d tl i ->
        ob [ natName .= n, natFun .= Null {- TODO fn -}
           , natFunTypes .= tys, natExamples .= Null {- TODO exs -},
             natDocs .= d, natTopLevel .= tl, inf i ]
      TConst d m c met i ->
        ob [ constArg .= d, modName .= m, constVal .= c, meta .= met, inf i ]
      TApp a _i -> toJSON a
      TVar n i -> ob [ var .= n, inf i ]
      TBinding bs b c i -> ob [pairs .= bs, body .= b, type' .= c, inf i]
      TObject o _i -> toJSON o
      TLiteral l i -> ob [literal .= l, inf i]
      TLam tlam _i -> toJSON tlam

      TGuard k i -> ob [guard' .= k, inf i]
      TUse u _i -> toJSON u
      TStep s tmeta i -> ob [body .= s, meta .= tmeta, inf i]
      TSchema sn smod smeta sfs i ->
        ob [ schemaName .= sn, modName .= smod, meta .= smeta
           , fields .= sfs, inf i ]
      TTable tn tmod th tty tmeta i ->
        ob [ tblName .= tn, modName .= tmod, hash' .= th, type' .= tty
           , meta .= tmeta, inf i ]
      TDynamic r m i ->
        ob [ dynRef .= r, dynMem .= m, inf i ]
      TModRef mr _i -> toJSON mr

    dec decval =
      let wo n f = withObject n f decval
          parseWithInfo f = uncurry f . (id &&& getInfo) <$> parseJSON decval
      in
        wo "Module" (\o -> TModule <$> o .: moduleDef <*> o .: body <*> inf' o)
        <|> wo "List"
            (\o -> TList <$> o .: list' <*> o .: type' <*> inf' o)
        <|> parseWithInfo TDef
      -- TNative intentionally not marshallable
        <|> wo "Const"
            (\o -> TConst <$> o .: constArg <*> o .: modName
              <*> o .: constVal <*> o .: meta <*> inf' o )
        <|> parseWithInfo TApp
        <|> wo "Var" (\o -> TVar <$>  o .: var <*> inf' o )
        <|> wo "Binding"
            (\o -> TBinding <$> o .: pairs <*> o .: body
              <*> o .: type' <*> inf' o)
        <|> parseWithInfo TObject
        <|> wo "Literal" (\o -> TLiteral <$> o .: literal <*> inf' o)
        <|> wo "Guard" (\o -> TGuard <$> o .: guard' <*> inf' o)
        <|> parseWithInfo TUse
        <|> parseWithInfo TLam
        <|> wo "Step"
            (\o -> TStep <$> o .: body <*> o .: meta <*> inf' o)
       --  parseWithInfo TStep
        <|> wo "Schema"
            (\o -> TSchema <$>  o .: schemaName <*> o .: modName
              <*> o .: meta <*> o .: fields <*> inf' o )
        <|> wo "Table"
            (\o -> TTable <$>  o .: tblName <*> o .: modName
              <*> o .: hash' <*> o .: type'
              <*> o .: meta <*> inf' o )
        <|> wo "Dynamic"
            (\o -> TDynamic <$> o .: dynRef <*> o .: dynMem <*> inf' o)

        <|> parseWithInfo TModRef

    ob = object
    moduleDef = "module"
    body = "body"
    meta = "meta"
    inf i = "i" .= i
    inf' o = o .: "i"
    list' = "list"
    type' = "type"
    natName = "name"
    natFun = "fun"
    natFunTypes = "types"
    natExamples = "examples"
    natDocs = "docs"
    natTopLevel = "tl"
    constArg = "arg"
    modName = "modname"
    constVal = "val"
    var = "var"
    pairs = "pairs"
    literal = "lit"
    guard' = "guard"
    schemaName = "name"
    fields = "fields"
    tblName = "name"
    hash' = "hash"
    dynRef = "dref"
    dynMem = "dmem"