module MyElm.Stringify exposing (arg2string, declaration2string, expose2string, expression2string, module2string, needsBrackets, qualifiedName2string, type2str, type2string) import MyElm.Types exposing (..) -- indentation indented : String -> String indented s = s |> String.split "\n" |> String.join "\n " |> String.append " " listLike : String -> String -> String -> List String -> String listLike before sep after l = let shouldBeMultiline = List.any (\ln -> List.length (String.split "\n" ln) > 1) l || List.foldl (\ln s -> s + String.length ln) 0 l > 100 in if shouldBeMultiline then "\n" ++ indented (before ++ " " ++ String.join ("\n" ++ sep) l ++ "\n" ++ after) else if after == "" && before == "" then String.join sep l else before ++ " " ++ String.join sep l ++ " " ++ after bodyIndent : String -> String bodyIndent str = if List.length (String.split "\n" str) > 1 then str else "\n " ++ str expose2string : Exposing -> String expose2string expose = case expose of ValueExposed val -> val TypeExposed tp -> tp TypeAndConstructors tp -> tp ++ "(..)" module2string : Module -> String module2string (Module { name, exposes, doc, imports, declarations }) = let header = "module " ++ name ++ " exposing (" ++ String.join ", " (List.map expose2string exposes) ++ ")\n\n" docstr = case doc of Just d -> "{-|" ++ d ++ "-}\n\n" Nothing -> "" imps = String.join "\n" imports ++ (if List.length imports > 0 then "\n\n\n" else "" ) decs = String.join "" <| List.map declaration2string declarations in header ++ docstr ++ imps ++ decs type2str : Bool -> Type -> String type2str needsBr tp = case tp of NamedType qualifiedName typeList -> if List.length typeList > 0 then if needsBr then "(" ++ qualifiedName2string qualifiedName ++ " " ++ String.join " " (List.map (type2str True) typeList) ++ ")" else qualifiedName2string qualifiedName ++ " " ++ String.join " " (List.map (type2str True) typeList) else qualifiedName2string qualifiedName RecordType branches -> "{ " ++ String.join ", " (List.map (\( name, typ ) -> name ++ " = " ++ type2str False typ) branches) ++ " }" FunctionType typeList -> let a = String.join " -> " (List.map (type2str False) typeList) in if needsBr then "(" ++ a ++ ")" else a TupleType typeList -> "( " ++ String.join ", " (List.map (type2str False) typeList) ++ " )" TypeVariable name -> name type2string = type2str False declaration2string : Declaration -> String declaration2string declaration = case declaration of CustomType name variables variants -> "type " ++ String.join " " (name :: variables) ++ "\n = " ++ String.join "\n | " (List.map (\( nm, args ) -> String.join " " (nm :: List.map (type2str True) args)) variants) ++ "\n\n\n" TypeAlias name variables aliased -> "type alias " ++ String.join " " (name :: variables) ++ "\n =" ++ type2string aliased ++ "\n\n\n" Comment str -> "{-|" ++ str ++ "}" ValueDeclaration name anno argList expression -> let decl = name ++ " " ++ String.join " " (List.map arg2string argList) ++ " =" ++ bodyIndent (expression2string expression) ++ "\n\n\n" in case anno of [] -> decl signature -> name ++ " : " ++ String.join " -> " (List.map type2string signature) ++ "\n" ++ decl arg2string : Argument -> String arg2string argument = case argument of Argument a -> a qualifiedName2string : QualifiedName -> String qualifiedName2string qualifiedName = let identifierToStr id = case id of Constructor _ s -> s ValueOrType s -> s in case qualifiedName of Local ident -> identifierToStr ident FullyQualified modPath ident -> String.join "." modPath ++ "." ++ identifierToStr ident Aliased _ alias_ ident -> alias_ ++ "." ++ identifierToStr ident Bare _ ident -> identifierToStr ident bracketify : Expression -> String bracketify arg = if needsBrackets arg then "(" ++ expression2string arg ++ ")" else expression2string arg isOperator : String -> Bool isOperator op = case op of "++" -> True "-" -> True "+" -> True "*" -> True "/" -> True "//" -> True "^" -> True "|>" -> True "<|" -> True _ -> False expression2string : Expression -> String expression2string expression = case expression of Call name args -> let nameStr = qualifiedName2string name in if isOperator nameStr then case args of a :: b :: rest -> case nameStr of "|>" -> listLike "" " |> " "" [ expression2string a, String.join " " (List.map expression2string (b :: rest)) ] _ -> expression2string a ++ " " ++ nameStr ++ " " ++ String.join " " (List.map expression2string (b :: rest)) _ -> "(" ++ nameStr ++ ") " ++ String.join " " (List.map bracketify args) else String.join " " (nameStr :: List.map (\arg -> if needsBrackets arg then "(" ++ expression2string arg ++ ")" else expression2string arg ) args ) Literal lit -> lit ListExpr expressions -> listLike "[" ", " "]" (List.map expression2string expressions) Tuple expressions -> listLike "(" ", " ")" (List.map expression2string expressions) Record branches -> listLike "{" ", " "}" (List.map (\( name, branch ) -> name ++ " = " ++ expression2string branch) branches) needsBrackets : Expression -> Bool needsBrackets expression = case expression of Call _ [] -> False Call _ _ -> True _ -> False