module Generate.JavaScript.Foreign (encode, decode) where import qualified Data.List as List import Language.ECMAScript3.Syntax import qualified AST.Expression.Optimized as Opt import qualified AST.Literal as Literal import qualified AST.Type as T import qualified AST.Variable as Var import Generate.JavaScript.Helpers ((<|), (==>), function, ref, var) import qualified Generate.JavaScript.Variable as Var -- ENCODE encode :: T.Canonical -> Expression () encode tipe = function ["v"] [ ReturnStmt () (Just (encodeHelp tipe (ref "v"))) ] encodeHelp :: T.Canonical -> Expression () -> Expression () encodeHelp tipe expr = case tipe of T.Aliased _ args t -> encodeHelp (T.dealias args t) expr T.Lambda _ _ -> error "encodeHelp: function" T.Var _ -> error "encodeHelp: type variable" T.Type name | Var.isPrim "Float" name -> expr | Var.isPrim "Int" name -> expr | Var.isPrim "Bool" name -> expr | Var.isPrim "String" name -> expr | Var.isJson name -> expr | Var.isTuple name -> NullLit () | otherwise -> error "encodeHelp: bad type" T.App (T.Type name) [arg] | Var.isMaybe name -> fromMaybe arg expr | Var.isList name -> fromList arg expr | Var.isArray name -> fromArray arg expr | otherwise -> error "encodeHelp: bad union type" T.App (T.Type name) args | Var.isTuple name -> fromTuple args expr T.App _ _ -> error "encodeHelp: bad union type" T.Record _ (Just _) -> error "encodeHelp: bad record" T.Record fields Nothing -> let convert (name, tipe) = name ==> encodeHelp tipe (DotRef () expr (var name)) in ObjectLit () (map convert fields) fromMaybe :: T.Canonical -> Expression () -> Expression () fromMaybe tipe expr = CondExpr () (InfixExpr () OpStrictEq (DotRef () expr (var "ctor")) (StringLit () "Nothing")) (NullLit ()) (encodeHelp tipe (DotRef () expr (var "_0"))) fromList :: T.Canonical -> Expression () -> Expression () fromList tipe expr = let array = Var.coreNative "List" "toArray" <| expr in DotRef () array (var "map") <| encode tipe fromArray :: T.Canonical -> Expression () -> Expression () fromArray tipe expr = let array = Var.coreNative "Array" "toJSArray" <| expr in DotRef () array (var "map") <| encode tipe fromTuple :: [T.Canonical] -> Expression () -> Expression () fromTuple types expr = let convert subType n = encodeHelp subType $ DotRef () expr (var ('_':show n)) in ArrayLit () (zipWith convert types [0..]) -- SUBSCRIPTIONS decode :: T.Canonical -> Opt.Expr decode tipe = case tipe of T.Lambda _ _ -> error "functions should not be allowed through input ports" T.Var _ -> error "type variables should not be allowed through input ports" T.Aliased _ args t -> decode (T.dealias args t) T.Type name | Var.isPrim "Float" name -> to "float" | Var.isPrim "Int" name -> to "int" | Var.isPrim "Bool" name -> to "bool" | Var.isPrim "String" name -> to "string" | Var.isJson name -> to "value" | Var.isTuple name -> toTuple0 | otherwise -> error "decode: bad type" T.App (T.Type name) [arg] | Var.isMaybe name -> toMaybe arg | Var.isList name -> to "list" <== [decode arg] | Var.isArray name -> to "array" <== [decode arg] | otherwise -> error "decode: bad union type" T.App (T.Type name) args | Var.isTuple name -> toTuple args T.App _ _ -> error "decode: bad union type" T.Record _ (Just _) -> error "decode: bad record" T.Record fields Nothing -> toRecord fields -- DECODE HELPERS inJsonDecode :: String -> Var.Canonical inJsonDecode name = Var.inCore ["Json","Decode"] name to :: String -> Opt.Expr to name = Opt.Var (inJsonDecode name) (=:) :: String -> Opt.Expr -> Opt.Expr (=:) name decoder = Opt.Binop (inJsonDecode ":=") (Opt.Literal (Literal.Str name)) decoder (<==) :: Opt.Expr -> [Opt.Expr] -> Opt.Expr (<==) func args = Opt.Call func args -- COMPLEX DECODE HELPERS toMaybe :: T.Canonical -> Opt.Expr toMaybe tipe = let maybe tag = Opt.Var (Var.inCore ["Maybe"] tag) in to "oneOf" <== [ Opt.ExplicitList [ to "null" <== [ maybe "Nothing" ] , to "map" <== [ maybe "Just", decode tipe ] ] ] toTuple0 :: Opt.Expr toTuple0 = to "null" <== [ Opt.Data "_Tuple0" [] ] toTuple :: [T.Canonical] -> Opt.Expr toTuple types = let size = length types args = map (\n -> 'x' : show n) [ 1 .. size ] makeTuple = Opt.Function args (Opt.Data ("_Tuple" ++ show size) (map (Opt.Var . Var.local) args)) in to ("tuple" ++ show size) <== (makeTuple : map decode types) toRecord :: [(String, T.Canonical)] -> Opt.Expr toRecord fields = let toFieldExpr (key, _) = (key, Opt.Var (Var.local key)) finalDecoder = to "succeed" <== [ Opt.Record (map toFieldExpr fields) ] andThen (key, tipe) decoder = to "andThen" <== [ key =: decode tipe, Opt.Function [key] decoder ] in List.foldr andThen finalDecoder fields