module Decoder.Expression exposing (decodeBool, expression) import Color import Decoder.Generic as Decode import Decoder.Helpers exposing (todo) import Json.Decode as D exposing (Decoder) import Lib import MyElm.Syntax exposing (Expression, call1, calln, float, int, list, pair, string) import String.Case exposing (toCamelCaseLower) expression = D.oneOf [ D.string |> D.map makeConstant , decodeBool , D.float |> D.map (float >> Lib.float) , D.int |> D.map (int >> Lib.int) , D.index 0 D.string |> D.andThen decodeExpression , D.index 0 D.int |> D.andThen (always (D.map (List.map int >> list >> Lib.floats) (D.list D.int))) , D.index 0 D.float |> D.andThen (always (D.map (List.map float >> list >> Lib.floats) (D.list D.float))) , todo ] decodeLiteral = D.oneOf [ D.string |> D.map makeConstant , decodeBool , D.float |> D.map (float >> Lib.float) , D.int |> D.map (int >> Lib.int) , todo ] makeConstant s = case s of "map" -> Lib.eValue "map" "viewport" -> Lib.eValue "viewport" "auto" -> Lib.eValue "auto" "center" -> Lib.eValue "center" "left" -> Lib.eValue "left" "right" -> Lib.eValue "right" "top" -> Lib.eValue "top" "bottom" -> Lib.eValue "bottom" "top-left" -> Lib.eValue "topLeft" "top-right" -> Lib.eValue "topRight" "bottom-left" -> Lib.eValue "bottomLeft" "bottom-right" -> Lib.eValue "bottomRight" "none" -> Lib.eValue "none" "width" -> Lib.eValue "width" "height" -> Lib.eValue "height" "both" -> Lib.eValue "both" "butt" -> Lib.eValue "butt" "round" -> Lib.eValue "rounded" "square" -> Lib.eValue "square" "bevel" -> Lib.eValue "bevel" "miter" -> Lib.eValue "miter" "point" -> Lib.eValue "point" "line-center" -> Lib.eValue "lineCenter" "line" -> Lib.eValue "line" "uppercase" -> Lib.eValue "uppercase" "lowercase" -> Lib.eValue "lowercase" "linear" -> Lib.eValue "linear" "nearest" -> Lib.eValue "nearest" "viewport-y" -> Lib.eValue "viewportY" "source" -> Lib.eValue "source" _ -> case Color.parse s of Ok { r, g, b, a } -> calln (Lib.eName "rgba") [ int r, int g, int b, float a ] Err err -> string s |> Lib.str decodeExpression funName = case funName of "literal" -> D.index 1 (D.oneOf [ D.list D.string |> D.map (\strs -> calln (Lib.eName "strings") [ list (List.map string strs) ]) , D.list D.float |> D.map (\floats -> calln (Lib.eName "floats") [ list (List.map float floats) ]) ] ) "match" -> D.oneOf [ D.index 2 D.string |> D.andThen (decodeMatch True) , D.index 2 D.float |> D.andThen (decodeMatch False) , D.index 2 (D.list D.string) |> D.andThen (decodeMatch True) , D.index 2 (D.list D.float) |> D.andThen (decodeMatch False) ] "exponential" -> D.map (\base -> calln (Lib.eName "Exponential") [ float base ]) (D.index 1 D.float) "interpolate" -> D.map3 (\interpolation options input -> Lib.pipelineCall "interpolate" (input :: interpolation :: options) ) (D.index 1 expression) (Decode.tail D.value |> D.map (List.drop 2) |> D.andThen (organizeArgs (D.map float D.float) [])) (D.index 2 expression) "step" -> D.map3 (\inp def stps -> Lib.pipelineCall "step" (inp :: def :: stps) ) (D.index 1 expression) (D.index 2 expression) (Decode.tail D.value |> D.map (List.drop 2) |> D.andThen (organizeArgs (D.map float D.float) [])) "case" -> D.map (calln (Lib.eName "conditionally")) (Decode.tail D.value |> D.andThen (organizeArgs expression [])) _ -> let fallback = Decode.tail expression |> D.map (\arguments -> case funName of "==" -> Lib.pipelineCall "isEqual" arguments "!=" -> Lib.pipelineCall "notEqual" arguments "!has" -> Lib.todo "!has is not supported" "!in" -> Lib.todo "!in is not supported" "in" -> Lib.todo "in is not supported" ">=" -> Lib.pipelineCall "greaterThanOrEqual" arguments ">" -> Lib.pipelineCall "greaterThan" arguments "<=" -> Lib.pipelineCall "lessThanOrEqual" arguments "<" -> Lib.pipelineCall "lessThan" arguments "concat" -> Lib.pipelineMultiCall "append" arguments "linear" -> calln (Lib.eName "Linear") arguments "rgb" -> calln (Lib.eName "makeRGBColor") arguments "rgba" -> calln (Lib.eName "makeRGBAColor") arguments "to-rgba" -> calln (Lib.eName "rgbaChannels") arguments "-" -> Lib.pipelineMultiCall "minus" arguments "*" -> Lib.pipelineMultiCall "multiply" arguments "+" -> Lib.pipelineMultiCall "plus" arguments "/" -> Lib.pipelineMultiCall "divideBy" arguments "%" -> Lib.pipelineMultiCall "modBy" arguments "^" -> Lib.pipelineMultiCall "raiseBy" arguments "get" -> if List.length arguments == 1 then calln (Lib.eName "getProperty") arguments else calln (Lib.eName "get") arguments "all" -> call1 (Lib.eName "all") (MyElm.Syntax.list arguments) "any" -> call1 (Lib.eName "any") (MyElm.Syntax.list arguments) _ -> calln (Lib.eName (toCamelCaseLower funName)) arguments ) in if String.toLower funName /= funName then D.oneOf [ D.map (\strs -> calln (Lib.eName "strings") [ list (List.map string strs) ]) <| D.list D.string , fallback ] else fallback decodeBool = D.bool |> D.map (\b -> if b then Lib.true else Lib.false ) decodeMatch : Bool -> any -> Decoder Expression decodeMatch isString _ = Decode.tail D.value |> D.andThen (\args -> case args of [] -> todo head :: tail -> D.map2 (\cond rest -> Lib.pipelineCall (if isString then "matchesStr" else "matchesFloat" ) (cond :: rest) ) (Decode.subdecode expression head) (organizeArgs (if isString then D.map string D.string else D.map float D.float ) [] (normalizeArgs tail) ) ) normalizeArgs args = case args of a :: b :: rest -> case D.decodeValue (D.list D.value) a of Err _ -> a :: b :: rest Ok xs -> List.concatMap (\x -> [ x, b ]) xs ++ normalizeArgs rest _ -> args organizeArgs inpDec accu args = case args of [] -> Decode.combine [ D.map list (List.reverse accu |> Decode.combine) ] [ default ] -> Decode.combine [ D.map list (List.reverse accu |> Decode.combine), Decode.subdecode expression default ] a :: b :: rest -> let newAccu = D.map2 pair (Decode.subdecode inpDec a) (Decode.subdecode expression b) :: accu in organizeArgs inpDec newAccu rest