{-# LANGUAGE OverloadedStrings #-} module Elm.Compiler.Type ( Type(..) , toString ) where import Control.Arrow (second) import Data.Aeson ((.:)) import qualified Data.Aeson as Json import qualified Data.Aeson.Types as Json import qualified Data.ByteString.Lazy.Char8 as BS import qualified Data.Text as Text import Text.PrettyPrint as P import qualified AST.Helpers as Help import qualified AST.Type as Type import qualified AST.Variable as Var import qualified Parse.Helpers as Parse import qualified Parse.Type as Type import qualified Reporting.Annotation as A data Type = Lambda Type Type | Var String | Type String | App Type [Type] | Record [(String, Type)] (Maybe Type) -- TO STRING data Context = None | ADT | Function toString :: Type -> String toString tipe = P.render (toDoc None tipe) toDoc :: Context -> Type -> P.Doc toDoc context tipe = case tipe of Lambda _ _ -> let t:ts = map (toDoc Function) (collectLambdas tipe) lambda = P.sep [ t, P.sep (map (P.text "->" <+>) ts) ] in case context of None -> lambda _ -> P.parens lambda Var name -> P.text name Type name -> P.text (if name == "_Tuple0" then "()" else name) App (Type name) args | name == "_Tuple0" -> P.text "()" | Help.isTuple name -> P.sep [ P.cat (zipWith (<+>) (P.lparen : repeat P.comma) (map (toDoc None) args)) , P.rparen ] | otherwise -> let adt = P.hang (P.text name) 2 (P.sep $ map (toDoc ADT) args) in case (context, args) of (ADT, _ : _) -> P.parens adt _ -> adt App _ _ -> error $ "Somehow ended up with an unexpected type, please post a minimal example that\n" ++ "reproduces this error to " Record _ _ -> case flattenRecord tipe of ([], Nothing) -> P.text "{}" (fields, Nothing) -> P.sep [ P.cat (zipWith (<+>) (P.lbrace : repeat P.comma) (map prettyField fields)) , P.rbrace ] (fields, Just x) -> P.hang (P.lbrace <+> P.text x <+> P.text "|") 4 (P.sep [ P.sep (P.punctuate P.comma (map prettyField fields)) , P.rbrace ] ) where prettyField (field, tipe) = P.text field <+> P.text ":" <+> toDoc None tipe collectLambdas :: Type -> [Type] collectLambdas tipe = case tipe of Lambda arg body -> arg : collectLambdas body _ -> [tipe] flattenRecord :: Type -> ( [(String, Type)], Maybe String ) flattenRecord tipe = case tipe of Var x -> ([], Just x) Record fields Nothing -> (fields, Nothing) Record fields (Just ext) -> let (fields',ext') = flattenRecord ext in (fields' ++ fields, ext') _ -> error "Trying to flatten ill-formed record." -- JSON for TYPE instance Json.ToJSON Type where toJSON tipe = Json.String (Text.unwords (Text.words (Text.pack (toString tipe)))) instance Json.FromJSON Type where parseJSON value = let failure _ = fail $ "Trying to decode a type string, but could not handle this value:\n" ++ BS.unpack (Json.encode value) in case value of Json.String text -> either failure (return . fromRawType) (Parse.iParse Type.expr (Text.unpack text)) Json.Object obj -> fromObject obj _ -> failure () fromObject :: Json.Object -> Json.Parser Type fromObject obj = do tag <- obj .: "tag" case (tag :: String) of "lambda" -> Lambda <$> obj .: "in" <*> obj .: "out" "var" -> Var <$> obj .: "name" "type" -> Type <$> obj .: "name" "app" -> App <$> obj .: "func" <*> obj .: "args" "record" -> Record <$> obj .: "fields" <*> obj .: "extension" _ -> fail $ "Error when decoding type with tag: " ++ tag fromRawType :: Type.Raw -> Type fromRawType (A.A _ astType) = case astType of Type.RLambda t1 t2 -> Lambda (fromRawType t1) (fromRawType t2) Type.RVar x -> Var x Type.RType var -> Type (Var.toString var) Type.RApp t ts -> App (fromRawType t) (map fromRawType ts) Type.RRecord fields ext -> Record (map (second fromRawType) fields) (fmap fromRawType ext)