aboutsummaryrefslogtreecommitdiffstats
path: root/style-generator
diff options
context:
space:
mode:
authorJakub Hampl <kopomir@gmail.com>2018-09-11 09:50:38 +0100
committerJakub Hampl <kopomir@gmail.com>2018-09-11 09:50:38 +0100
commit075a1730211ed26e227bc8de6ad9a032048e66ee (patch)
tree75eb3b1cf1255df3f093c3c891341ca912a0ac54 /style-generator
parent2e9381479d484d383238493306421327623bc4a2 (diff)
Add style generator
Diffstat (limited to 'style-generator')
-rw-r--r--style-generator/elm.json33
-rw-r--r--style-generator/src/Color.elm376
-rw-r--r--style-generator/src/Decoder.elm776
-rw-r--r--style-generator/src/Main.elm138
-rw-r--r--style-generator/src/Writer.elm640
5 files changed, 1963 insertions, 0 deletions
diff --git a/style-generator/elm.json b/style-generator/elm.json
new file mode 100644
index 0000000..4f116f6
--- /dev/null
+++ b/style-generator/elm.json
@@ -0,0 +1,33 @@
+{
+ "type": "application",
+ "source-directories": [
+ "src"
+ ],
+ "elm-version": "0.19.0",
+ "dependencies": {
+ "direct": {
+ "elm/browser": "1.0.0",
+ "elm/core": "1.0.0",
+ "elm/html": "1.0.0",
+ "elm/http": "1.0.0",
+ "elm/json": "1.0.0",
+ "elm/parser": "1.1.0",
+ "elm-community/list-extra": "8.0.0",
+ "stil4m/elm-syntax": "6.1.0",
+ "stil4m/structured-writer": "1.0.2",
+ "the-sett/elm-string-case": "1.0.2"
+ },
+ "indirect": {
+ "elm/time": "1.0.0",
+ "elm/url": "1.0.0",
+ "elm/virtual-dom": "1.0.0",
+ "elm-community/json-extra": "4.0.0",
+ "rtfeldman/elm-hex": "1.0.0",
+ "rtfeldman/elm-iso8601-date-strings": "1.1.2"
+ }
+ },
+ "test-dependencies": {
+ "direct": {},
+ "indirect": {}
+ }
+} \ No newline at end of file
diff --git a/style-generator/src/Color.elm b/style-generator/src/Color.elm
new file mode 100644
index 0000000..b4e8b73
--- /dev/null
+++ b/style-generator/src/Color.elm
@@ -0,0 +1,376 @@
+module Color exposing (parse)
+
+import Bitwise exposing (shiftLeftBy)
+import Parser exposing ((|.), (|=), Parser, backtrackable, end, keyword, oneOf, spaces, succeed, symbol)
+
+
+type alias Color =
+ { r : Int, g : Int, b : Int, a : Float }
+
+
+parser : Parser Color
+parser =
+ oneOf
+ [ keywords
+ , hsla
+
+ -- , rgba
+ -- , hex
+ ]
+ |. end
+
+
+fromHSLA hue sat light alpha =
+ let
+ ( h, s, l ) =
+ ( toFloat hue / 360, toFloat sat / 100, toFloat light / 100 )
+
+ m2 =
+ if l <= 0.5 then
+ l * (s + 1)
+ else
+ l + s - l * s
+
+ m1 =
+ l * 2 - m2
+
+ r =
+ hueToRgb (h + 1 / 3)
+
+ g =
+ hueToRgb h
+
+ b =
+ hueToRgb (h - 1 / 3)
+
+ hueToRgb h__ =
+ let
+ h_ =
+ if h__ < 0 then
+ h__ + 1
+ else if h__ > 1 then
+ h__ - 1
+ else
+ h__
+ in
+ if h_ * 6 < 1 then
+ m1 + (m2 - m1) * h_ * 6
+ else if h_ * 2 < 1 then
+ m2
+ else if h_ * 3 < 2 then
+ m1 + (m2 - m1) * (2 / 3 - h_) * 6
+ else
+ m1
+ in
+ Color (r * 255 |> floor) (g * 255 |> floor) (b * 255 |> floor) alpha
+
+
+hsla : Parser Color
+hsla =
+ succeed fromHSLA
+ |. oneOf [ keyword "hsla", keyword "hsl" ]
+ |. symbol "("
+ |= angle
+ |. spaces
+ |. symbol ","
+ |. spaces
+ |= percentage
+ |. spaces
+ |. symbol ","
+ |. spaces
+ |= percentage
+ |= oneOf
+ [ succeed identity
+ |. symbol ","
+ |. spaces
+ |= Parser.float
+ , succeed 1
+ ]
+ |. symbol ")"
+
+
+angle =
+ Parser.map round Parser.float
+
+
+percentage =
+ Parser.map round Parser.float
+ |. symbol "%"
+
+
+fromHexString : String -> Parser Color
+fromHexString hexString =
+ case String.toList hexString of
+ [ '#', r, g, b ] ->
+ fromHex8 ( r, r ) ( g, g ) ( b, b ) ( 'f', 'f' )
+
+ [ r, g, b ] ->
+ fromHex8 ( r, r ) ( g, g ) ( b, b ) ( 'f', 'f' )
+
+ [ '#', r, g, b, a ] ->
+ fromHex8 ( r, r ) ( g, g ) ( b, b ) ( a, a )
+
+ [ r, g, b, a ] ->
+ fromHex8 ( r, r ) ( g, g ) ( b, b ) ( a, a )
+
+ [ '#', r1, r2, g1, g2, b1, b2 ] ->
+ fromHex8 ( r1, r2 ) ( g1, g2 ) ( b1, b2 ) ( 'f', 'f' )
+
+ [ r1, r2, g1, g2, b1, b2 ] ->
+ fromHex8 ( r1, r2 ) ( g1, g2 ) ( b1, b2 ) ( 'f', 'f' )
+
+ [ '#', r1, r2, g1, g2, b1, b2, a1, a2 ] ->
+ fromHex8 ( r1, r2 ) ( g1, g2 ) ( b1, b2 ) ( a1, a2 )
+
+ [ r1, r2, g1, g2, b1, b2, a1, a2 ] ->
+ fromHex8 ( r1, r2 ) ( g1, g2 ) ( b1, b2 ) ( a1, a2 )
+
+ _ ->
+ Parser.problem "Invalid color"
+
+
+maybeToParser : Maybe a -> Parser a
+maybeToParser aMaybe =
+ case aMaybe of
+ Just a ->
+ succeed a
+
+ Nothing ->
+ Parser.problem "something went wrong"
+
+
+fromHex8 : ( Char, Char ) -> ( Char, Char ) -> ( Char, Char ) -> ( Char, Char ) -> Parser Color
+fromHex8 ( r1, r2 ) ( g1, g2 ) ( b1, b2 ) ( a1, a2 ) =
+ Maybe.map4
+ (\r g b a ->
+ Color
+ r
+ g
+ b
+ (toFloat a / 255)
+ )
+ (hex2ToInt r1 r2)
+ (hex2ToInt g1 g2)
+ (hex2ToInt b1 b2)
+ (hex2ToInt a1 a2)
+ |> maybeToParser
+
+
+hex2ToInt : Char -> Char -> Maybe Int
+hex2ToInt c1 c2 =
+ Maybe.map2 (\v1 v2 -> shiftLeftBy 4 v1 + v2) (hexToInt c1) (hexToInt c2)
+
+
+hexToInt : Char -> Maybe Int
+hexToInt char =
+ case Char.toLower char of
+ '0' ->
+ Just 0
+
+ '1' ->
+ Just 1
+
+ '2' ->
+ Just 2
+
+ '3' ->
+ Just 3
+
+ '4' ->
+ Just 4
+
+ '5' ->
+ Just 5
+
+ '6' ->
+ Just 6
+
+ '7' ->
+ Just 7
+
+ '8' ->
+ Just 8
+
+ '9' ->
+ Just 9
+
+ 'a' ->
+ Just 10
+
+ 'b' ->
+ Just 11
+
+ 'c' ->
+ Just 12
+
+ 'd' ->
+ Just 13
+
+ 'e' ->
+ Just 14
+
+ 'f' ->
+ Just 15
+
+ _ ->
+ Nothing
+
+
+keywords : Parser Color
+keywords =
+ oneOf
+ [ fromHexString "#000000" |. keyword "black"
+ , fromHexString "#c0c0c0" |. keyword "silver"
+ , fromHexString "#808080" |. keyword "gray"
+ , fromHexString "#ffffff" |. keyword "white"
+ , fromHexString "#800000" |. keyword "maroon"
+ , fromHexString "#ff0000" |. keyword "red"
+ , fromHexString "#800080" |. keyword "purple"
+ , fromHexString "#ff00ff" |. keyword "fuchsia"
+ , fromHexString "#008000" |. keyword "green"
+ , fromHexString "#00ff00" |. keyword "lime"
+ , fromHexString "#808000" |. keyword "olive"
+ , fromHexString "#ffff00" |. keyword "yellow"
+ , fromHexString "#000080" |. keyword "navy"
+ , fromHexString "#0000ff" |. keyword "blue"
+ , fromHexString "#008080" |. keyword "teal"
+ , fromHexString "#00ffff" |. keyword "aqua"
+ , fromHexString "#ffa500" |. keyword "orange"
+ , fromHexString "#f0f8ff" |. keyword "aliceblue"
+ , fromHexString "#faebd7" |. keyword "antiquewhite"
+ , fromHexString "#7fffd4" |. keyword "aquamarine"
+ , fromHexString "#f0ffff" |. keyword "azure"
+ , fromHexString "#f5f5dc" |. keyword "beige"
+ , fromHexString "#ffe4c4" |. keyword "bisque"
+ , fromHexString "#ffebcd" |. keyword "blanchedalmond"
+ , fromHexString "#8a2be2" |. keyword "blueviolet"
+ , fromHexString "#a52a2a" |. keyword "brown"
+ , fromHexString "#deb887" |. keyword "burlywood"
+ , fromHexString "#5f9ea0" |. keyword "cadetblue"
+ , fromHexString "#7fff00" |. keyword "chartreuse"
+ , fromHexString "#d2691e" |. keyword "chocolate"
+ , fromHexString "#ff7f50" |. keyword "coral"
+ , fromHexString "#6495ed" |. keyword "cornflowerblue"
+ , fromHexString "#fff8dc" |. keyword "cornsilk"
+ , fromHexString "#dc143c" |. keyword "crimson"
+ , fromHexString "#00ffff" |. keyword "cyan"
+ , fromHexString "#00008b" |. keyword "darkblue"
+ , fromHexString "#008b8b" |. keyword "darkcyan"
+ , fromHexString "#b8860b" |. keyword "darkgoldenrod"
+ , fromHexString "#a9a9a9" |. keyword "darkgray"
+ , fromHexString "#006400" |. keyword "darkgreen"
+ , fromHexString "#a9a9a9" |. keyword "darkgrey"
+ , fromHexString "#bdb76b" |. keyword "darkkhaki"
+ , fromHexString "#8b008b" |. keyword "darkmagenta"
+ , fromHexString "#556b2f" |. keyword "darkolivegreen"
+ , fromHexString "#ff8c00" |. keyword "darkorange"
+ , fromHexString "#9932cc" |. keyword "darkorchid"
+ , fromHexString "#8b0000" |. keyword "darkred"
+ , fromHexString "#e9967a" |. keyword "darksalmon"
+ , fromHexString "#8fbc8f" |. keyword "darkseagreen"
+ , fromHexString "#483d8b" |. keyword "darkslateblue"
+ , fromHexString "#2f4f4f" |. keyword "darkslategray"
+ , fromHexString "#2f4f4f" |. keyword "darkslategrey"
+ , fromHexString "#00ced1" |. keyword "darkturquoise"
+ , fromHexString "#9400d3" |. keyword "darkviolet"
+ , fromHexString "#ff1493" |. keyword "deeppink"
+ , fromHexString "#00bfff" |. keyword "deepskyblue"
+ , fromHexString "#696969" |. keyword "dimgray"
+ , fromHexString "#696969" |. keyword "dimgrey"
+ , fromHexString "#1e90ff" |. keyword "dodgerblue"
+ , fromHexString "#b22222" |. keyword "firebrick"
+ , fromHexString "#fffaf0" |. keyword "floralwhite"
+ , fromHexString "#228b22" |. keyword "forestgreen"
+ , fromHexString "#dcdcdc" |. keyword "gainsboro"
+ , fromHexString "#f8f8ff" |. keyword "ghostwhite"
+ , fromHexString "#ffd700" |. keyword "gold"
+ , fromHexString "#daa520" |. keyword "goldenrod"
+ , fromHexString "#adff2f" |. keyword "greenyellow"
+ , fromHexString "#808080" |. keyword "grey"
+ , fromHexString "#f0fff0" |. keyword "honeydew"
+ , fromHexString "#ff69b4" |. keyword "hotpink"
+ , fromHexString "#cd5c5c" |. keyword "indianred"
+ , fromHexString "#4b0082" |. keyword "indigo"
+ , fromHexString "#fffff0" |. keyword "ivory"
+ , fromHexString "#f0e68c" |. keyword "khaki"
+ , fromHexString "#e6e6fa" |. keyword "lavender"
+ , fromHexString "#fff0f5" |. keyword "lavenderblush"
+ , fromHexString "#7cfc00" |. keyword "lawngreen"
+ , fromHexString "#fffacd" |. keyword "lemonchiffon"
+ , fromHexString "#add8e6" |. keyword "lightblue"
+ , fromHexString "#f08080" |. keyword "lightcoral"
+ , fromHexString "#e0ffff" |. keyword "lightcyan"
+ , fromHexString "#fafad2" |. keyword "lightgoldenrodyellow"
+ , fromHexString "#d3d3d3" |. keyword "lightgray"
+ , fromHexString "#90ee90" |. keyword "lightgreen"
+ , fromHexString "#d3d3d3" |. keyword "lightgrey"
+ , fromHexString "#ffb6c1" |. keyword "lightpink"
+ , fromHexString "#ffa07a" |. keyword "lightsalmon"
+ , fromHexString "#20b2aa" |. keyword "lightseagreen"
+ , fromHexString "#87cefa" |. keyword "lightskyblue"
+ , fromHexString "#778899" |. keyword "lightslategray"
+ , fromHexString "#778899" |. keyword "lightslategrey"
+ , fromHexString "#b0c4de" |. keyword "lightsteelblue"
+ , fromHexString "#ffffe0" |. keyword "lightyellow"
+ , fromHexString "#32cd32" |. keyword "limegreen"
+ , fromHexString "#faf0e6" |. keyword "linen"
+ , fromHexString "#ff00ff" |. keyword "magenta"
+ , fromHexString "#66cdaa" |. keyword "mediumaquamarine"
+ , fromHexString "#0000cd" |. keyword "mediumblue"
+ , fromHexString "#ba55d3" |. keyword "mediumorchid"
+ , fromHexString "#9370db" |. keyword "mediumpurple"
+ , fromHexString "#3cb371" |. keyword "mediumseagreen"
+ , fromHexString "#7b68ee" |. keyword "mediumslateblue"
+ , fromHexString "#00fa9a" |. keyword "mediumspringgreen"
+ , fromHexString "#48d1cc" |. keyword "mediumturquoise"
+ , fromHexString "#c71585" |. keyword "mediumvioletred"
+ , fromHexString "#191970" |. keyword "midnightblue"
+ , fromHexString "#f5fffa" |. keyword "mintcream"
+ , fromHexString "#ffe4e1" |. keyword "mistyrose"
+ , fromHexString "#ffe4b5" |. keyword "moccasin"
+ , fromHexString "#ffdead" |. keyword "navajowhite"
+ , fromHexString "#fdf5e6" |. keyword "oldlace"
+ , fromHexString "#6b8e23" |. keyword "olivedrab"
+ , fromHexString "#ff4500" |. keyword "orangered"
+ , fromHexString "#da70d6" |. keyword "orchid"
+ , fromHexString "#eee8aa" |. keyword "palegoldenrod"
+ , fromHexString "#98fb98" |. keyword "palegreen"
+ , fromHexString "#afeeee" |. keyword "paleturquoise"
+ , fromHexString "#db7093" |. keyword "palevioletred"
+ , fromHexString "#ffefd5" |. keyword "papayawhip"
+ , fromHexString "#ffdab9" |. keyword "peachpuff"
+ , fromHexString "#cd853f" |. keyword "peru"
+ , fromHexString "#ffc0cb" |. keyword "pink"
+ , fromHexString "#dda0dd" |. keyword "plum"
+ , fromHexString "#b0e0e6" |. keyword "powderblue"
+ , fromHexString "#bc8f8f" |. keyword "rosybrown"
+ , fromHexString "#4169e1" |. keyword "royalblue"
+ , fromHexString "#8b4513" |. keyword "saddlebrown"
+ , fromHexString "#fa8072" |. keyword "salmon"
+ , fromHexString "#f4a460" |. keyword "sandybrown"
+ , fromHexString "#2e8b57" |. keyword "seagreen"
+ , fromHexString "#fff5ee" |. keyword "seashell"
+ , fromHexString "#a0522d" |. keyword "sienna"
+ , fromHexString "#87ceeb" |. keyword "skyblue"
+ , fromHexString "#6a5acd" |. keyword "slateblue"
+ , fromHexString "#708090" |. keyword "slategray"
+ , fromHexString "#708090" |. keyword "slategrey"
+ , fromHexString "#fffafa" |. keyword "snow"
+ , fromHexString "#00ff7f" |. keyword "springgreen"
+ , fromHexString "#4682b4" |. keyword "steelblue"
+ , fromHexString "#d2b48c" |. keyword "tan"
+ , fromHexString "#d8bfd8" |. keyword "thistle"
+ , fromHexString "#ff6347" |. keyword "tomato"
+ , fromHexString "#40e0d0" |. keyword "turquoise"
+ , fromHexString "#ee82ee" |. keyword "violet"
+ , fromHexString "#f5deb3" |. keyword "wheat"
+ , fromHexString "#f5f5f5" |. keyword "whitesmoke"
+ , fromHexString "#9acd32" |. keyword "yellowgreen"
+ , fromHexString "#663399" |. keyword "rebeccapurple"
+ , succeed (Color 0 0 0 0) |. keyword "transparent"
+ ]
+
+
+parse : String -> Result String Color
+parse string =
+ Parser.run parser string |> Result.mapError Parser.deadEndsToString
diff --git a/style-generator/src/Decoder.elm b/style-generator/src/Decoder.elm
new file mode 100644
index 0000000..65dc0a2
--- /dev/null
+++ b/style-generator/src/Decoder.elm
@@ -0,0 +1,776 @@
+module Decoder exposing (styleCode)
+
+import Color
+import Elm.Syntax.Declaration exposing (Declaration(..))
+import Elm.Syntax.Exposing exposing (Exposing(..), TopLevelExpose(..))
+import Elm.Syntax.Expression exposing (Expression(..), RecordSetter)
+import Elm.Syntax.Infix exposing (InfixDirection(..))
+import Elm.Syntax.Module exposing (Module(..))
+import Elm.Syntax.Node exposing (Node(..))
+import Elm.Syntax.Pattern
+import Elm.Syntax.Range exposing (emptyRange)
+import Elm.Syntax.TypeAnnotation exposing (TypeAnnotation(..))
+import Json.Decode as D exposing (Decoder)
+import Json.Encode
+import String.Case exposing (toCamelCaseLower)
+import Writer
+
+
+node =
+ Node emptyRange
+
+
+wrapNodes =
+ List.map node
+
+
+styleCode : Decoder String
+styleCode =
+ D.map (file >> Writer.writeFile >> Writer.write) style
+
+
+declarations styleDec =
+ [ FunctionDeclaration
+ { documentation = Nothing
+ , signature =
+ Just
+ (node
+ { name = node "style"
+ , typeAnnotation = node (Typed (node ( [], "Style" )) [])
+ }
+ )
+ , declaration =
+ node
+ { name = node "style"
+ , arguments = []
+ , expression =
+ node <|
+ Application <|
+ wrapNodes
+ [ FunctionOrValue [] "Style"
+ , RecordExpr styleDec
+ ]
+ }
+ }
+ ]
+
+
+file styleDec =
+ { moduleDefinition =
+ node
+ (NormalModule
+ { moduleName = node [ "Style" ]
+ , exposingList = node (Explicit [ node (FunctionExpose "style") ])
+ }
+ )
+ , imports =
+ [ node
+ { moduleName = node [ "Mapbox", "Style" ]
+ , moduleAlias = Just (node [ "Style" ])
+ , exposingList = Just (Explicit [ node (TypeExpose { name = "Style", open = Just emptyRange }) ])
+ }
+ , node
+ { moduleName = node [ "Mapbox", "Source" ]
+ , moduleAlias = Just (node [ "Source" ])
+ , exposingList = Nothing
+ }
+ , node
+ { moduleName = node [ "Mapbox", "Layer" ]
+ , moduleAlias = Just (node [ "Layer" ])
+ , exposingList = Nothing
+ }
+ , node
+ { moduleName = node [ "Mapbox", "Expression" ]
+ , moduleAlias = Just (node [ "E" ])
+ , exposingList = Just (Explicit [ node (FunctionExpose "str"), node (FunctionExpose "float"), node (FunctionExpose "int"), node (FunctionExpose "true"), node (FunctionExpose "false") ])
+ }
+ ]
+ , declarations = List.map node (declarations styleDec)
+ , comments = []
+ }
+
+
+style : Decoder (List (Node RecordSetter))
+style =
+ D.map5
+ (\transition light layers sources misc ->
+ [ node ( node "transition", transition )
+ , node ( node "light", light )
+ , node ( node "layers", layers )
+ , node ( node "sources", sources )
+ , node ( node "misc", misc )
+ ]
+ )
+ (D.oneOf
+ [ D.field "transition" decodeTransition
+ , valueDecoder "Style" "defaultTransition"
+ ]
+ )
+ (D.oneOf
+ [ D.field "light" decodeLight
+ , valueDecoder "Style" "defaultLight"
+ ]
+ )
+ (D.field "layers" decodeLayers)
+ (D.field "sources" decodeSources)
+ decodeMisc
+
+
+decodeTransition : Decoder (Node Expression)
+decodeTransition =
+ D.map2
+ (\duration delay ->
+ node
+ (RecordExpr
+ [ node ( node "duration", node (Integer duration) )
+ , node ( node "delay", node (Integer delay) )
+ ]
+ )
+ )
+ (D.oneOf [ D.field "duration" D.int, D.succeed 300 ])
+ (D.oneOf [ D.field "delay" D.int, D.succeed 0 ])
+
+
+decodeLight : Decoder (Node Expression)
+decodeLight =
+ valueDecoder "Style" "defaultLight"
+
+
+addBogusRange index (Node _ v) =
+ Node { start = { row = index, column = 0 }, end = { row = index + 1, column = 0 } } v
+
+
+decodeLayers : Decoder (Node Expression)
+decodeLayers =
+ D.list decodeLayer
+ |> D.map (\layers -> node (ListExpr (List.indexedMap addBogusRange layers)))
+
+
+layerDecodeHelp t =
+ D.map3 (\id source attrs -> call "Layer" t [ str id, str source, list attrs ]) (D.field "id" D.string) (D.field "source" D.string) decodeAttrs
+
+
+decodeLayer : Decoder (Node Expression)
+decodeLayer =
+ D.field "type" D.string
+ |> D.andThen
+ (\t ->
+ case t of
+ "background" ->
+ D.map2 (\id attrs -> call "Layer" "background" [ str id, list attrs ]) (D.field "id" D.string) decodeAttrs
+
+ "fill" ->
+ layerDecodeHelp "fill"
+
+ "symbol" ->
+ layerDecodeHelp "symbol"
+
+ "line" ->
+ layerDecodeHelp "line"
+
+ "raster" ->
+ layerDecodeHelp "raster"
+
+ "circle" ->
+ layerDecodeHelp "circle"
+
+ "fill-extrusion" ->
+ layerDecodeHelp "fillExtrusion"
+
+ "heatmap" ->
+ layerDecodeHelp "heatmap"
+
+ "hillshade" ->
+ layerDecodeHelp "hillshade"
+
+ other ->
+ D.fail ("Layer type " ++ t ++ " not supported")
+ )
+
+
+decodeAttrs : Decoder (List (Node Expression))
+decodeAttrs =
+ D.map3 (\top paint layout -> top ++ paint ++ layout) (D.keyValuePairs D.value) (D.field "paint" (D.keyValuePairs D.value)) (D.field "layout" (D.keyValuePairs D.value))
+ |> D.andThen
+ (List.filterMap
+ (\( attrName, attrValue ) ->
+ case attrName of
+ "id" ->
+ Nothing
+
+ "type" ->
+ Nothing
+
+ "source" ->
+ Nothing
+
+ "paint" ->
+ Nothing
+
+ "layout" ->
+ Nothing
+
+ "source-layer" ->
+ decodeAttr "sourceLayer" (D.map str D.string) attrValue
+
+ "minzoom" ->
+ decodeAttr "minzoom" (D.map float D.float) attrValue
+
+ "maxzoom" ->
+ decodeAttr "maxzoom" (D.map float D.float) attrValue
+
+ "filter" ->
+ decodeAttr "filter" (D.oneOf [ decodeLegacyFilter, decodeValue ]) attrValue
+
+ other ->
+ decodeAttr (toCamelCaseLower attrName) decodeValue attrValue
+ )
+ >> combine
+ )
+ |> D.map (List.indexedMap addBogusRange)
+
+
+decodeAttr : String -> Decoder (Node Expression) -> D.Value -> Maybe (Decoder (Node Expression))
+decodeAttr attrName expressionNodeDecoder attrValue =
+ Just
+ (D.decodeValue expressionNodeDecoder attrValue
+ |> resultToDecoder
+ |> D.map (\v -> call "Layer" (toCamelCaseLower attrName) [ v ])
+ )
+
+
+resultToDecoder : Result D.Error a -> Decoder a
+resultToDecoder res =
+ case res of
+ Ok a ->
+ D.succeed a
+
+ Err e ->
+ D.fail (D.errorToString e)
+
+
+decodeBool : Decoder (Node Expression)
+decodeBool =
+ D.bool
+ |> D.map
+ (\b ->
+ if b then
+ evalue "true"
+ else
+ evalue "false"
+ )
+
+
+decodeValue : Decoder (Node Expression)
+decodeValue =
+ D.oneOf
+ [ D.string |> D.map makeConstant
+ , decodeBool
+ , D.float |> D.map (Floatable >> node >> ecall "float")
+ , D.int |> D.map (Integer >> node >> ecall "int")
+ , D.index 0 D.string |> D.andThen decodeExpression
+ , todo
+ ]
+ |> D.map (ParenthesizedExpression >> node)
+
+
+makeConstant s =
+ case s of
+ "map" ->
+ value "E" "anchorMap"
+
+ "viewport" ->
+ value "E" "anchorViewport"
+
+ "auto" ->
+ value "E" "anchorAuto"
+
+ "center" ->
+ value "E" "positionCenter"
+
+ "left" ->
+ value "E" "positionLeft"
+
+ "right" ->
+ value "E" "positionRight"
+
+ "top" ->
+ value "E" "positionTop"
+
+ "bottom" ->
+ value "E" "positionBottom"
+
+ "topRight" ->
+ value "E" "positionTopRight"
+
+ "topLeft" ->
+ value "E" "positionTopLeft"
+
+ "bottomLeft" ->
+ value "E" "positionBottomLeft"
+
+ "bottomRight" ->
+ value "E" "positionBottomRight"
+
+ "none" ->
+ value "E" "textFitNone"
+
+ "width" ->
+ value "E" "textFitWidth"
+
+ "height" ->
+ value "E" "textFitHeight"
+
+ "both" ->
+ value "E" "textFitBoth"
+
+ "butt" ->
+ value "E" "lineCapButt"
+
+ "round" ->
+ value "E" "lineCapRound"
+
+ "square" ->
+ value "E" "lineCapSquare"
+
+ "bevel" ->
+ value "E" "lineJoinBevel"
+
+ "miter" ->
+ value "E" "lineJoinMiter"
+
+ "point" ->
+ value "E" "symbolPlacementPoint"
+
+ "line-center" ->
+ value "E" "symbolPlacementLineCenter"
+
+ "line" ->
+ value "E" "symbolPlacementLine"
+
+ "uppercase" ->
+ value "E" "textTransformUppercase"
+
+ "lowercase" ->
+ value "E" "textTransformLowercase"
+
+ "linear" ->
+ value "E" "rasterResamplingLinear"
+
+ "nearest" ->
+ value "E" "rasterResamplingNearest"
+
+ _ ->
+ case Color.parse s of
+ Ok { r, g, b, a } ->
+ call "E" "rgba" [ integer r, integer g, integer b, float a ]
+
+ Err err ->
+ str s |> ecall "str"
+
+
+
+-- legacy filter
+
+
+decodeLegacyFilter : Decoder (Node Expression)
+decodeLegacyFilter =
+ let
+ decodeProp =
+ D.index 1 D.string
+ |> D.map
+ (\prop ->
+ case prop of
+ "$type" ->
+ value "E" "geometryType"
+
+ "$id" ->
+ value "E" "id"
+
+ _ ->
+ call "E" "getProperty" [ ecall "str" (str prop) ]
+ )
+
+ decodeVal =
+ D.index 2 <|
+ D.oneOf
+ [ D.map (str >> ecall "str") D.string
+ , D.map (float >> ecall "float") D.float
+ , decodeBool
+ ]
+
+ decodeVals =
+ D.list <|
+ D.oneOf
+ [ D.map (str >> ecall "str") D.string
+ , D.map (float >> ecall "float") D.float
+ , decodeBool
+ ]
+ in
+ D.index 0 D.string
+ |> D.andThen
+ (\filter ->
+ case filter of
+ "all" ->
+ decodeTail decodeLegacyFilter |> D.map (\filters -> call "E" "all" [ list filters ])
+
+ "any" ->
+ decodeTail decodeLegacyFilter |> D.map (\filters -> call "E" "any" [ list filters ])
+
+ "none" ->
+ decodeTail decodeLegacyFilter |> D.map (\filters -> call "E" "all" [ list (List.map (\f -> call "E" "not" [ f ]) filters) ])
+
+ "has" ->
+ D.index 1 D.string |> D.map (\prop -> call "E" "hasProperty" [ ecall "str" (str prop) ])
+
+ "!has" ->
+ D.index 1 D.string |> D.map (\prop -> call "E" "not" [ call "E" "hasProperty" [ ecall "str" (str prop) ] ])
+
+ "==" ->
+ D.map2 (\prop val -> pipelineCall "E" "isEqual" [ prop, val ]) decodeProp decodeVal
+
+ "!=" ->
+ D.map2 (\prop val -> pipelineCall "E" "notEqual" [ prop, val ]) decodeProp decodeVal
+
+ ">" ->
+ D.map2 (\prop val -> pipelineCall "E" "greaterThan" [ prop, val ]) decodeProp decodeVal
+
+ ">=" ->
+ D.map2 (\prop val -> pipelineCall "E" "greaterThanOrEqual" [ prop, val ]) decodeProp decodeVal
+
+ "<" ->
+ D.map2 (\prop val -> pipelineCall "E" "lessThan" [ prop, val ]) decodeProp decodeVal
+
+ "<=" ->
+ D.map2 (\prop val -> pipelineCall "E" "lessThanOrEqual" [ prop, val ]) decodeProp decodeVal
+
+ "in" ->
+ D.map2
+ (\prop values ->
+ List.drop 2 values
+ |> List.map (\v -> pipelineCall "E" "isEqual" [ prop, v ])
+ |> list
+ |> List.singleton
+ |> call "E" "any"
+ )
+ decodeProp
+ decodeVals
+
+ "!in" ->
+ D.map2
+ (\prop values ->
+ List.drop 2 values
+ |> List.map (\v -> pipelineCall "E" "notEqual" [ prop, v ])
+ |> list
+ |> List.singleton
+ |> call "E" "all"
+ )
+ decodeProp
+ decodeVals
+
+ _ ->
+ D.fail "not actually a legacy filter"
+ )
+
+
+
+-- Expressions
+
+
+decodeTail : Decoder a -> Decoder (List a)
+decodeTail itemDecoder =
+ D.list D.value
+ |> D.andThen
+ (\l ->
+ case l of
+ [] ->
+ D.fail "Can't get tail of empty"
+
+ head :: t ->
+ List.map (subdecode itemDecoder) t |> combine
+ )
+
+
+subdecode : Decoder a -> D.Value -> Decoder a
+subdecode d v =
+ D.decodeValue d v |> resultToDecoder
+
+
+decodeMatch : Bool -> any -> Decoder (Node Expression)
+decodeMatch isString _ =
+ decodeTail D.value
+ |> D.andThen
+ (\args ->
+ case args of
+ [] ->
+ todo
+
+ head :: tail ->
+ D.map2
+ (\cond rest ->
+ parens
+ (node
+ (OperatorApplication "|>"
+ Right
+ cond
+ (call "E"
+ (if isString then
+ "matchesStr"
+ else
+ "matchesFloat"
+ )
+ rest
+ )
+ )
+ )
+ )
+ (subdecode decodeValue head)
+ (organizeArgs
+ (if isString then
+ D.map str D.string
+ else
+ D.map float D.float
+ )
+ []
+ tail
+ )
+ )
+
+
+organizeArgs : Decoder (Node Expression) -> List (Decoder (Node Expression)) -> List D.Value -> Decoder (List (Node Expression))
+organizeArgs inpDec accu args =
+ case args of
+ [] ->
+ combine [ D.map list (List.reverse accu |> combine) ]
+
+ [ default ] ->
+ combine [ D.map list (List.reverse accu |> combine), subdecode decodeValue default ]
+
+ a :: b :: rest ->
+ let
+ newAccu =
+ D.map2
+ (\inp out ->
+ parens (node (TupledExpression [ inp, out ]))
+ )
+ (subdecode inpDec a)
+ (subdecode decodeValue b)
+ :: accu
+ in
+ organizeArgs inpDec newAccu rest
+
+
+decodeExpression : String -> Decoder (Node Expression)
+decodeExpression funName =
+ case funName of
+ "literal" ->
+ D.index 1
+ (D.oneOf
+ [ D.list D.string |> D.map (\strs -> call "E" "strings" [ list (List.map str strs) ])
+ , D.list D.float |> D.map (\floats -> call "E" "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)
+ ]
+
+ "exponential" ->
+ D.map (\base -> call "E" "Exponential" [ float base ]) (D.index 1 D.float)
+
+ "interpolate" ->
+ D.map3
+ (\interpolation options input ->
+ pipelineCall "E" "interpolate" (input :: interpolation :: options)
+ )
+ (D.index 1 decodeValue)
+ (decodeTail D.value |> D.map (List.drop 2) |> D.andThen (organizeArgs (D.map float D.float) []))
+ (D.index 2 decodeValue)
+
+ "step" ->
+ D.map3
+ (\inp def stps ->
+ pipelineCall "E" "step" (inp :: def :: stps)
+ )
+ (D.index 1 decodeValue)
+ (D.index 2 decodeValue)
+ (decodeTail D.value |> D.map (List.drop 2) |> D.andThen (organizeA