@@ -0,0 +1,312 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE OverloadedStrings #-}
10+import Prelude hiding (maybe, null)
11+import Control.Monad (foldM, zipWithM)
12+import qualified Data.Text as Text
13+import Data.Text (Text)
15+import qualified AST.Expression.Optimized as Opt
16+import qualified AST.Literal as Literal
17+import qualified AST.Module.Name as ModuleName
18+import qualified AST.Type as T
19+import qualified AST.Variable as Var
20+import qualified Optimize.DecisionTree as DT
21+import qualified Optimize.Environment as Env
28+toEncoder :: T.Canonical -> Env.Optimizer Opt.Expr
31+ T.Aliased _ args t ->
32+ toEncoder (T.dealias args t)
35+ error "toEncoder: function"
38+ error "toEncoder: type variable"
41+ | Var.isPrim "Float" name -> encode "float"
42+ | Var.isPrim "Int" name -> encode "int"
43+ | Var.isPrim "Bool" name -> encode "bool"
44+ | Var.isPrim "String" name -> encode "string"
45+ | Var.isJson name -> encode "string"
46+ | Var.isTuple name -> encode "null"
47+ | otherwise -> error "toEncoder: bad type"
49+ T.App (T.Type name) [arg]
50+ | Var.isMaybe name -> encodeMaybe arg
51+ | Var.isList name -> encodeList arg
52+ | Var.isArray name -> encodeArray arg
53+ | otherwise -> error "toEncoder: bad union type"
55+ T.App (T.Type name) args
56+ | Var.isTuple name -> encodeTuple args
59+ error "toEncoder: bad union type"
61+ T.Record _ (Just _) ->
62+ error "toEncoder: bad record"
64+ T.Record fields Nothing ->
66+ convert (field, typ) =
67+ do encoder <- toEncoder typ
70+ , Opt.Call encoder [ Opt.Access (Opt.VarLocal "r") field ]
73+ do record <- traverse convert fields
74+ return $ Opt.Function ["r"] (Opt.Record record)
81+encodeMaybe :: T.Canonical -> Env.Optimizer Opt.Expr
83+ do null <- encode "null"
84+ encoder <- toEncoder tipe
86+ let nothing = Var.inCore "Maybe" "Nothing"
87+ let test = (DT.Empty, DT.Constructor nothing)
88+ let just = Opt.Call encoder [ Opt.CtorAccess (Opt.VarLocal "m") 0 ]
89+ let leaf expr = Opt.Leaf (Opt.Inline expr)
91+ return $ Opt.Function ["m"] $
92+ Opt.Case "m" (Opt.Chain [test] (leaf null) (leaf just)) []
95+encodeList :: T.Canonical -> Env.Optimizer Opt.Expr
97+ do list <- encode "list"
98+ map_ <- core "List" "map"
99+ encoder <- toEncoder tipe
101+ Opt.Function ["xs"] $
102+ Opt.Call list [ Opt.Call map_ [ encoder, Opt.VarLocal "xs" ] ]
105+encodeArray :: T.Canonical -> Env.Optimizer Opt.Expr
107+ do array <- encode "array"
108+ map_ <- core "Array" "map"
109+ encoder <- toEncoder tipe
111+ Opt.Function ["xs"] $
112+ Opt.Call array [ Opt.Call map_ [ encoder, Opt.VarLocal "xs" ] ]
115+encodeTuple :: [T.Canonical] -> Env.Optimizer Opt.Expr
118+ convert tipe index =
119+ do encoder <- toEncoder tipe
121+ Opt.Call encoder [ Opt.CtorAccess (Opt.VarLocal "t") index ]
123+ do list <- encode "list"
124+ entries <- zipWithM convert tipes [ 0 .. length tipes ]
125+ return $ Opt.Function ["t"] $ Opt.Call list entries
132+toDecoder :: T.Canonical -> Env.Optimizer Opt.Expr
136+ error "functions should not be allowed through input ports"
139+ error "type variables should not be allowed through input ports"
141+ T.Aliased _ args t ->
142+ toDecoder (T.dealias args t)
145+ | Var.isPrim "Float" name -> decode "float"
146+ | Var.isPrim "Int" name -> decode "int"
147+ | Var.isPrim "Bool" name -> decode "bool"
148+ | Var.isPrim "String" name -> decode "string"
149+ | Var.isJson name -> decode "value"
150+ | Var.isTuple name -> decodeTuple0
151+ | otherwise -> error "toDecoder: bad type"
153+ T.App (T.Type name) [arg]
154+ | Var.isMaybe name -> decodeMaybe arg
155+ | Var.isList name -> decodeList arg
156+ | Var.isArray name -> decodeArray arg
157+ | otherwise -> error "toDecoder: bad union type"
159+ T.App (T.Type name) args
160+ | Var.isTuple name -> decodeTuple args
163+ error "toDecoder: bad union type"
165+ T.Record _ (Just _) ->
166+ error "toDecoder: bad record"
168+ T.Record fields Nothing ->
176+decodeMaybe :: T.Canonical -> Env.Optimizer Opt.Expr
178+ do let nothing = Var.Global (ModuleName.inCore "Maybe") "Nothing"
179+ let just = Var.Global (ModuleName.inCore "Maybe") "Just"
181+ Env.register nothing
184+ oneOf <- decode "oneOf"
185+ null <- decode "null"
186+ map_ <- decode "map"
188+ subDecoder <- toDecoder tipe
193+ [ Opt.Call null [ Opt.VarGlobal nothing ]
194+ , Opt.Call map_ [ Opt.VarGlobal just, subDecoder ]
202+decodeList :: T.Canonical -> Env.Optimizer Opt.Expr
204+ do list <- decode "list"
205+ decoder <- toDecoder tipe
206+ return $ Opt.Call list [ decoder ]
213+decodeArray :: T.Canonical -> Env.Optimizer Opt.Expr
215+ do array <- decode "array"
216+ decoder <- toDecoder tipe
217+ return $ Opt.Call array [ decoder ]
224+decodeTuple0 :: Env.Optimizer Opt.Expr
226+ do null <- decode "null"
227+ return (Opt.Call null [ Opt.Ctor "_Tuple0" [] ])
230+decodeTuple :: [T.Canonical] -> Env.Optimizer Opt.Expr
237+ zip [ 0 .. size - 1] types
241+ (Text.pack ("_Tuple" ++ show size))
242+ (map (Opt.VarLocal . indexToName . fst) entries)
244+ do succeed <- decode "succeed"
245+ foldM indexAndThen (Opt.Call succeed [tuple]) entries
248+indexToName :: Int -> Text
250+ Text.pack ('x' : show index)
253+indexAndThen :: Opt.Expr -> (Int, T.Canonical) -> Env.Optimizer Opt.Expr
254+indexAndThen decoder (i, tipe) =
255+ do andThen <- decode "andThen"
256+ index <- decode "index"
257+ typeDecoder <- toDecoder tipe
260+ [ Opt.Function [indexToName i] decoder
261+ , Opt.Call index [ Opt.Literal (Literal.IntNum i), typeDecoder ]
269+decodeRecord :: [(Text, T.Canonical)] -> Env.Optimizer Opt.Expr
270+decodeRecord fields =
272+ toFieldExpr (key, _) =
273+ (key, Opt.VarLocal key)
276+ Opt.Record (map toFieldExpr fields)
278+ do succeed <- decode "succeed"
279+ foldM fieldAndThen (Opt.Call succeed [record]) fields
282+fieldAndThen :: Opt.Expr -> (Text, T.Canonical) -> Env.Optimizer Opt.Expr
283+fieldAndThen decoder (key, tipe) =
284+ do andThen <- decode "andThen"
285+ field <- decode "field"
286+ typeDecoder <- toDecoder tipe
289+ [ Opt.Function [key] decoder
290+ , Opt.Call field [ Opt.Literal (Literal.Str key), typeDecoder ]
298+core :: Text -> Text -> Env.Optimizer Opt.Expr
300+ do let var = Var.Global (ModuleName.inCore home) name
302+ return (Opt.VarGlobal var)
305+encode :: Text -> Env.Optimizer Opt.Expr
307+ core "Json.Encode" name
310+decode :: Text -> Env.Optimizer Opt.Expr
312+ core "Json.Decode" name