File: JSON.hs

package info (click to toggle)
haskell-derive 2.5.16-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 460 kB
  • sloc: haskell: 3,686; makefile: 5
file content (165 lines) | stat: -rw-r--r-- 6,320 bytes parent folder | download
1
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
-- |
-- Copyright:   (c) Bertram Felgenhauer 2009
-- License:     BSD3
-- Stability:   experimental
-- Portability: portable
--
-- Derive 'Text.JSON' instances.
--
-- Unlike Text.JSON.Generics, single constructor types are /not/ handled
-- specially. Every value is encoded as an object with a single field,
-- with the constructor name as key and the values as its contents.
--
-- If the constructor is a record, the contents is an Object with the
-- field names as keys. Otherwise, the contents is an array.

module Data.Derive.JSON (makeJSON) where

import qualified Language.Haskell as H
import Language.Haskell (
    Exp, Pat, Alt, CtorDecl, Decl, FullDataDecl, FieldDecl, BangType, Stmt,
    (~=), var, pVar, con, strE, strP, apps, qname, sl,
    ctorDeclFields, ctorDeclName, dataDeclCtors)

{-
import "json" Text.JSON
import Text.JSON.Types

example :: Custom
instance JSON a => JSON (Sample a) where
    readJSON (JSObject x)   = $(readJSON)
    readJSON _              = Error "..."
    showJSON (First)        = $(showJSON 0)
    showJSON (Second x1 x2) = $(showJSON 1)
    showJSON (Third x1)     = $(showJSON 2)
-}

-- GENERATED START

import Data.Derive.DSL.DSL
import Data.Derive.Internal.Derivation

makeJSON :: Derivation
makeJSON = derivationCustomDSL "JSON" custom $
    List [Instance ["JSON"] "JSON" (List [App "InsDecl" (List [App
    "FunBind" (List [List [App "Match" (List [App "Ident" (List [
    String "readJSON"]),List [App "PParen" (List [App "PApp" (List [
    App "UnQual" (List [App "Ident" (List [String "JSObject"])]),List
    [App "PVar" (List [App "Ident" (List [String "x"])])]])])],App
    "Nothing" (List []),App "UnGuardedRhs" (List [App "SpliceExp" (
    List [App "ParenSplice" (List [App "Var" (List [App "UnQual" (List
    [App "Ident" (List [String "readJSON"])])])])])]),App "BDecls" (
    List [List []])]),App "Match" (List [App "Ident" (List [String
    "readJSON"]),List [App "PWildCard" (List [])],App "Nothing" (List
    []),App "UnGuardedRhs" (List [App "App" (List [App "Con" (List [
    App "UnQual" (List [App "Ident" (List [String "Error"])])]),App
    "Lit" (List [App "String" (List [String "..."])])])]),App "BDecls"
    (List [List []])])]])]),App "InsDecl" (List [App "FunBind" (List [
    MapCtor (App "Match" (List [App "Ident" (List [String "showJSON"])
    ,List [App "PParen" (List [App "PApp" (List [App "UnQual" (List [
    App "Ident" (List [CtorName])]),MapField (App "PVar" (List [App
    "Ident" (List [Concat (List [String "x",ShowInt FieldIndex])])]))]
    )])],App "Nothing" (List []),App "UnGuardedRhs" (List [App
    "SpliceExp" (List [App "ParenSplice" (List [App "App" (List [App
    "Var" (List [App "UnQual" (List [App "Ident" (List [String
    "showJSON"])])]),App "Lit" (List [App "Int" (List [CtorIndex])])])
    ])])]),App "BDecls" (List [List []])]))])])])]
-- GENERATED STOP

-- ^ 'Derivation' for 'JSON'

custom :: FullDataDecl -> [Decl] -> [Decl]
custom = customSplice splice

splice :: FullDataDecl -> Exp -> Exp
splice d x | x ~= "readJSON" = mkRead d
splice d (H.App x (H.Lit (H.Int y))) | x~= "showJSON" = mkShow d y
splice _ e = error $ "makeJSON: unrecognized splice: " ++ show e

------------------------------------------------------------------------------
-- showJSON

mkShow :: FullDataDecl -> Integer -> Exp
mkShow d y = let
    hasFields = any (not . null . fst) (ctorDeclFields c)
    c = dataDeclCtors (snd d) !! fromInteger y
    mkFields = if hasFields then mkShowRecordFields else mkShowPlainFields
  in
    mkJSObject $ H.List
        [H.Tuple H.Boxed [strE (ctorDeclName c), mkFields (ctorDeclFields c)]]

mkShowPlainFields :: FieldDecl -> Exp
mkShowPlainFields fs = mkJSArray $ H.List
    [var "showJSON" `H.App` xi | xi <- vars "x" fs]

mkShowRecordFields :: FieldDecl -> Exp
mkShowRecordFields fs = mkJSObject $ H.List
    [ H.Tuple H.Boxed [strE fn, var "showJSON" `H.App` xi]
    | ((fn, _), xi) <- zip fs (vars "x" fs)]

------------------------------------------------------------------------------
-- readJSON

mkRead :: FullDataDecl -> Exp
mkRead (_, d) = let
    readError = con "Error" `H.App` strE "malformed JSON for type ...: ..."
  in
    H.Case (var "fromJSObject" `H.App` var "x") $
    map mkReadCtor (dataDeclCtors d) ++
    [H.Alt H.sl H.PWildCard (H.UnGuardedAlt readError) (H.BDecls [])]

mkReadCtor :: CtorDecl -> Alt
mkReadCtor c = let
    cn = ctorDeclName c
    fs = ctorDeclFields c
    hasFields = any (not . null . fst) fs
    body | hasFields = mkReadRecord cn fs
         | otherwise = mkReadPlain cn fs
  in
    H.Alt sl (H.PList [H.PTuple H.Boxed [strP cn, pVar "y"]])
         (H.UnGuardedAlt body) (H.BDecls [])

mkReadRecord :: String -> FieldDecl -> Exp
mkReadRecord cn fs = H.Do $
    [H.Generator sl (H.PApp (qname "JSObject") [pVar "z"])
          (var "return" `H.App` var "y")] ++
    [H.LetStmt $ H.BDecls [H.PatBind sl (pVar "d") Nothing
          (H.UnGuardedRhs $ var "fromJSObject" `H.App` var "z")
          (H.BDecls [])]] ++
    zipWith (mkReadRecordField cn) (pVars "x" fs) fs ++
    mkReadTrailer cn fs

mkReadRecordField :: String -> Pat -> (String, BangType) -> Stmt
mkReadRecordField cn xi (fn, _) = H.Generator sl xi $
    apps (var "maybe") [
        var "fail" `H.App` strE (unwords ["readJSON: missing field", fn,
                                          "while decoding a", cn]),
        var "return",
        apps (var "lookup") [strE fn, var "d"]]

mkReadPlain :: String -> FieldDecl -> Exp
mkReadPlain cn fs = H.Do $
    [H.Generator sl (H.PApp (qname "JSArray") [H.PList (pVars "x" fs)])
        (var "return" `H.App` var "y")] ++
    mkReadTrailer cn fs

mkReadTrailer :: String -> FieldDecl -> [Stmt]
mkReadTrailer cn fs =
    [ H.Generator sl yi (var "readJSON" `H.App` xi)
    | (xi, yi) <- zip (vars "x" fs) (pVars "y" fs)] ++
    [H.Qualifier $ var "return" `H.App` apps (con cn) (vars "y" fs)]

------------------------------------------------------------------------------
-- utilites

mkJSObject :: Exp -> Exp
mkJSObject e = con "JSObject" `H.App` (var "toJSObject" `H.App` e)

mkJSArray :: Exp -> Exp
mkJSArray e = con "JSArray" `H.App` e

vars :: String -> FieldDecl -> [Exp]
vars pre fs = [var (pre ++ show i) | i <- [1..length fs]]

pVars :: String -> FieldDecl -> [Pat]
pVars pre fs = [pVar (pre ++ show i) | i <- [1..length fs]]