From 10bdf5ca26f5ce6e14be75cdb751acc2cd39685a Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Mon, 27 Jul 2020 16:59:41 -0400 Subject: [PATCH 1/9] Implement the List module in the SDK. #105 --- src/Morphir/SDK/List.elm | 656 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 655 insertions(+), 1 deletion(-) diff --git a/src/Morphir/SDK/List.elm b/src/Morphir/SDK/List.elm index b07b5fe2b..d4d77e3d0 100644 --- a/src/Morphir/SDK/List.elm +++ b/src/Morphir/SDK/List.elm @@ -1,6 +1,624 @@ -module Morphir.SDK.List exposing (..) +module Morphir.SDK.List exposing + ( singleton, repeat, range, cons + , map, indexedMap, foldl, foldr, filter, filterMap + , length, reverse, member, all, any, maximum, minimum, sum, product + , append, concat, concatMap, intersperse, map2, map3, map4, map5 + , sort, sortBy, sortWith + , isEmpty, head, tail, take, drop, partition, unzip + , last, init + , innerJoin, leftJoin, rightJoin + ) +{-| This module is an extension of the `List` module in `elm/core`. It includes all functions from `elm/core` so that +you can use it without changing existing code. Simply add the following import to use: + import Morphir.SDK.List as List + +You can create a `List` in Elm with the `[1,2,3]` syntax, so lists are +used all over the place. This module has a bunch of functions to help you work +with them! + + +# Create + +@docs singleton, repeat, range, cons + + +# Transform + +@docs map, indexedMap, foldl, foldr, filter, filterMap + + +# Utilities + +@docs length, reverse, member, all, any, maximum, minimum, sum, product + + +# Combine + +@docs append, concat, concatMap, intersperse, map2, map3, map4, map5 + + +# Sort + +@docs sort, sortBy, sortWith + + +# Deconstruct + +@docs isEmpty, head, tail, take, drop, partition, unzip +@docs last, init + + +# Joins + +@docs innerJoin, leftJoin, rightJoin + +-} + +-- CREATE + + +{-| Create a list with only one element: +singleton 1234 == [1234] +singleton "hi" == ["hi"] +-} +singleton : a -> List a +singleton value = + List.singleton value + + +{-| Create a list with _n_ copies of a value: +repeat 3 (0,0) == [(0,0),(0,0),(0,0)] +-} +repeat : Int -> a -> List a +repeat n value = + List.repeat n value + + +{-| Create a list of numbers, every element increasing by one. +You give the lowest and highest number that should be in the list. +range 3 6 == [3, 4, 5, 6] +range 3 3 == [3] +range 6 3 == [] +-} +range : Int -> Int -> List Int +range lo hi = + List.range lo hi + + +{-| Add an element to the front of a list. +1 :: [2,3] == [1,2,3] +1 :: [] == [1] +This operator is pronounced _cons_ for historical reasons, but you can think +of it like pushing an entry onto a stack. +-} +cons : a -> List a -> List a +cons = + (::) + + + +-- TRANSFORM + + +{-| Apply a function to every element of a list. +map sqrt [1,4,9] == [1,2,3] +map not [True,False,True] == [False,True,False] +So `map func [ a, b, c ]` is the same as `[ func a, func b, func c ]` +-} +map : (a -> b) -> List a -> List b +map f xs = + List.map f xs + + +{-| Same as `map` but the function is also applied to the index of each +element (starting at zero). +indexedMap Tuple.pair ["Tom","Sue","Bob"] == [ (0,"Tom"), (1,"Sue"), (2,"Bob") ] +-} +indexedMap : (Int -> a -> b) -> List a -> List b +indexedMap f xs = + List.indexedMap f xs + + +{-| Reduce a list from the left. +foldl (+) 0 [1,2,3] == 6 +foldl (::) [][1,2,3] == [3,2,1] +So `foldl step state [1,2,3]` is like saying: +state +|> step 1 +|> step 2 +|> step 3 +-} +foldl : (a -> b -> b) -> b -> List a -> b +foldl func acc list = + List.foldl func acc list + + +{-| Reduce a list from the right. +foldr (+) 0 [1,2,3] == 6 +foldr (::) [][1,2,3] == [1,2,3] +So `foldr step state [1,2,3]` is like saying: +state +|> step 3 +|> step 2 +|> step 1 +-} +foldr : (a -> b -> b) -> b -> List a -> b +foldr fn acc ls = + List.foldr fn acc ls + + +{-| Keep elements that satisfy the test. +filter isEven [1,2,3,4,5,6] == [2,4,6] +-} +filter : (a -> Bool) -> List a -> List a +filter isGood list = + List.filter isGood list + + +{-| Filter out certain values. For example, maybe you have a bunch of strings +from an untrusted source and you want to turn them into numbers: +numbers : List Int +numbers = +filterMap String.toInt ["3", "hi", "12", "4th", "May"] +-- numbers == [3, 12] +-} +filterMap : (a -> Maybe b) -> List a -> List b +filterMap f xs = + List.filterMap f xs + + +maybeCons : (a -> Maybe b) -> a -> List b -> List b +maybeCons f mx xs = + case f mx of + Just x -> + cons x xs + + Nothing -> + xs + + + +-- UTILITIES + + +{-| Determine the length of a list. +length [1,2,3] == 3 +-} +length : List a -> Int +length xs = + foldl (\_ i -> i + 1) 0 xs + + +{-| Reverse a list. +reverse [1,2,3,4] == [4,3,2,1] +-} +reverse : List a -> List a +reverse list = + foldl cons [] list + + +{-| Figure out whether a list contains a value. +member 9 [1,2,3,4] == False +member 4 [1,2,3,4] == True +-} +member : a -> List a -> Bool +member x xs = + any (\a -> a == x) xs + + +{-| Determine if all elements satisfy some test. +all isEven [2,4] == True +all isEven [2,3] == False +all isEven [] == True +-} +all : (a -> Bool) -> List a -> Bool +all isOkay list = + not (any (not << isOkay) list) + + +{-| Determine if any elements satisfy some test. +any isEven [2,3] == True +any isEven [1,3] == False +any isEven [] == False +-} +any : (a -> Bool) -> List a -> Bool +any isOkay list = + case list of + [] -> + False + + x :: xs -> + -- note: (isOkay x || any isOkay xs) would not get TCO + if isOkay x then + True + + else + any isOkay xs + + +{-| Find the maximum element in a non-empty list. +maximum [1,4,2] == Just 4 +maximum [] == Nothing +-} +maximum : List comparable -> Maybe comparable +maximum list = + case list of + x :: xs -> + Just (foldl max x xs) + + _ -> + Nothing + + +{-| Find the minimum element in a non-empty list. +minimum [3,2,1] == Just 1 +minimum [] == Nothing +-} +minimum : List comparable -> Maybe comparable +minimum list = + case list of + x :: xs -> + Just (foldl min x xs) + + _ -> + Nothing + + +{-| Get the sum of the list elements. +sum [1,2,3] == 6 +sum [1,1,1] == 3 +sum [] == 0 +-} +sum : List number -> number +sum numbers = + foldl (+) 0 numbers + + +{-| Get the product of the list elements. +product [2,2,2] == 8 +product [3,3,3] == 27 +product [] == 1 +-} +product : List number -> number +product numbers = + foldl (*) 1 numbers + + + +-- COMBINE + + +{-| Put two lists together. +append [1,1,2][3,5,8] == [1,1,2,3,5,8] +append ['a','b']['c'] == ['a','b','c'] +You can also use [the `(++)` operator](Basics#++) to append lists. +-} +append : List a -> List a -> List a +append xs ys = + case ys of + [] -> + xs + + _ -> + foldr cons ys xs + + +{-| Concatenate a bunch of lists into a single list: +concat [[1,2],[3],[4,5]] == [1,2,3,4,5] +-} +concat : List (List a) -> List a +concat lists = + foldr append [] lists + + +{-| Map a given function onto a list and flatten the resulting lists. +concatMap f xs == concat (map f xs) +-} +concatMap : (a -> List b) -> List a -> List b +concatMap f list = + concat (map f list) + + +{-| Places the given value between all members of the given list. +intersperse "on" ["turtles","turtles","turtles"] == ["turtles","on","turtles","on","turtles"] +-} +intersperse : a -> List a -> List a +intersperse sep xs = + case xs of + [] -> + [] + + hd :: tl -> + let + step x rest = + cons sep (cons x rest) + + spersed = + foldr step [] tl + in + cons hd spersed + + +{-| Combine two lists, combining them with the given function. +If one list is longer, the extra elements are dropped. +totals : List Int -> List Int -> List Int +totals xs ys = +List.map2 (+) xs ys +-- totals [1,2,3][4,5,6] == [5,7,9] +pairs : List a -> List b -> List (a,b) +pairs xs ys = +List.map2 Tuple.pair xs ys +-- pairs ["alice","bob","chuck"][2,5,7,8] +-- == [("alice",2),("bob",5),("chuck",7)] +-} +map2 : (a -> b -> result) -> List a -> List b -> List result +map2 = + Elm.Kernel.List.map2 + + +{-| -} +map3 : (a -> b -> c -> result) -> List a -> List b -> List c -> List result +map3 = + Elm.Kernel.List.map3 + + +{-| -} +map4 : (a -> b -> c -> d -> result) -> List a -> List b -> List c -> List d -> List result +map4 = + Elm.Kernel.List.map4 + + +{-| -} +map5 : (a -> b -> c -> d -> e -> result) -> List a -> List b -> List c -> List d -> List e -> List result +map5 = + Elm.Kernel.List.map5 + + + +-- SORT + + +{-| Sort values from lowest to highest +sort [3,1,5] == [1,3,5] +-} +sort : List comparable -> List comparable +sort xs = + sortBy identity xs + + +{-| Sort values by a derived property. +alice = { name="Alice", height=1.62 } +bob = { name="Bob" , height=1.85 } +chuck = { name="Chuck", height=1.76 } +sortBy .name [chuck,alice,bob] == [alice,bob,chuck] +sortBy .height [chuck,alice,bob] == [alice,chuck,bob] +sortBy String.length ["mouse","cat"] == ["cat","mouse"] +-} +sortBy : (a -> comparable) -> List a -> List a +sortBy = + Elm.Kernel.List.sortBy + + +{-| Sort values with a custom comparison function. +sortWith flippedComparison [1,2,3,4,5] == [5,4,3,2,1] +flippedComparison a b = +case compare a b of +LT -> GT +EQ -> EQ +GT -> LT +This is also the most general sort function, allowing you +to define any other: `sort == sortWith compare` +-} +sortWith : (a -> a -> Order) -> List a -> List a +sortWith = + Elm.Kernel.List.sortWith + + + +-- DECONSTRUCT + + +{-| Determine if a list is empty. +isEmpty [] == True +**Note:** It is usually preferable to use a `case` to test this so you do not +forget to handle the `(x :: xs)` case as well! +-} +isEmpty : List a -> Bool +isEmpty xs = + case xs of + [] -> + True + + _ -> + False + + +{-| Extract the first element of a list. +head [1,2,3] == Just 1 +head [] == Nothing +**Note:** It is usually preferable to use a `case` to deconstruct a `List` +because it gives you `(x :: xs)` and you can work with both subparts. +-} +head : List a -> Maybe a +head list = + case list of + x :: xs -> + Just x + + [] -> + Nothing + + +{-| Extract the rest of the list. +tail [1,2,3] == Just [2,3] +tail [] == Nothing +**Note:** It is usually preferable to use a `case` to deconstruct a `List` +because it gives you `(x :: xs)` and you can work with both subparts. +-} +tail : List a -> Maybe (List a) +tail list = + case list of + x :: xs -> + Just xs + + [] -> + Nothing + + +{-| Take the first _n_ members of a list. +take 2 [1,2,3,4] == [1,2] +-} +take : Int -> List a -> List a +take n list = + takeFast 0 n list + + +takeFast : Int -> Int -> List a -> List a +takeFast ctr n list = + if n <= 0 then + [] + + else + case ( n, list ) of + ( _, [] ) -> + list + + ( 1, x :: _ ) -> + [ x ] + + ( 2, x :: y :: _ ) -> + [ x, y ] + + ( 3, x :: y :: z :: _ ) -> + [ x, y, z ] + + ( _, x :: y :: z :: w :: tl ) -> + if ctr > 1000 then + cons x (cons y (cons z (cons w (takeTailRec (n - 4) tl)))) + + else + cons x (cons y (cons z (cons w (takeFast (ctr + 1) (n - 4) tl)))) + + _ -> + list + + +takeTailRec : Int -> List a -> List a +takeTailRec n list = + reverse (takeReverse n list []) + + +takeReverse : Int -> List a -> List a -> List a +takeReverse n list kept = + if n <= 0 then + kept + + else + case list of + [] -> + kept + + x :: xs -> + takeReverse (n - 1) xs (cons x kept) + + +{-| Drop the first _n_ members of a list. +drop 2 [1,2,3,4] == [3,4] +-} +drop : Int -> List a -> List a +drop n list = + if n <= 0 then + list + + else + case list of + [] -> + list + + x :: xs -> + drop (n - 1) xs + + +{-| Partition a list based on some test. The first list contains all values +that satisfy the test, and the second list contains all the value that do not. +partition (\\x -> x < 3) [0,1,2,3,4,5] == ([0,1,2], [3,4,5]) +partition isEven [0,1,2,3,4,5] == ([0,2,4], [1,3,5]) +-} +partition : (a -> Bool) -> List a -> ( List a, List a ) +partition pred list = + let + step x ( trues, falses ) = + if pred x then + ( cons x trues, falses ) + + else + ( trues, cons x falses ) + in + foldr step ( [], [] ) list + + +{-| Decompose a list of tuples into a tuple of lists. + + unzip [ ( 0, True ), ( 17, False ), ( 1337, True ) ] == ( [ 0, 17, 1337 ], [ True, False, True ] ) + +-} +unzip : List ( a, b ) -> ( List a, List b ) +unzip pairs = + let + step ( x, y ) ( xs, ys ) = + ( cons x xs, cons y ys ) + in + foldr step ( [], [] ) pairs + + +{-| Returns the last element of a list. + + last [ 1, 2, 3 ] == Just 3 + + last [] == Nothing + +-} +last : List a -> Maybe a +last list = + list + |> List.reverse + |> List.head + + +{-| Returns all elements of a list except for the last. + + init [ 1, 2, 3 ] == Just [ 1, 2 ] + + init [] == Nothing + +-} +init : List a -> Maybe (List a) +init list = + list + |> List.reverse + |> List.tail + |> Maybe.map List.reverse + + +{-| Simulates a SQL inner-join. + + dataSetA = + [ ( 1, "a" ), ( 2, "b" ) ] + + dataSetB = + [ ( 3, "C" ), ( 2, "B" ) ] + + dataSetA + |> innerJoin dataSetB + (\a b -> + Tuple.first a == Tuple.first b + ) == + [ ( ( 2, "b" ), ( 2, "B" ) ) + ] + +-} innerJoin : List b -> (a -> b -> Bool) -> List a -> List ( a, b ) innerJoin listB onPredicate listA = listA @@ -18,6 +636,24 @@ innerJoin listB onPredicate listA = ) +{-| Simulates a SQL left-outer-join. + + dataSetA = + [ ( 1, "a" ), ( 2, "b" ) ] + + dataSetB = + [ ( 3, "C" ), ( 2, "B" ) ] + + dataSetA + |> leftJoin dataSetB + (\a b -> + Tuple.first a == Tuple.first b + ) == + [ ( ( 1, "a" ), Nothing ) + , ( ( 2, "b" ), Just ( 2, "B" ) ) + ] + +-} leftJoin : List b -> (a -> b -> Bool) -> List a -> List ( a, Maybe b ) leftJoin listB onPredicate listA = listA @@ -43,6 +679,24 @@ leftJoin listB onPredicate listA = ) +{-| Simulates a SQL right-outer-join. + + dataSetA = + [ ( 1, "a" ), ( 2, "b" ) ] + + dataSetB = + [ ( 3, "C" ), ( 2, "B" ) ] + + dataSetA + |> rightJoin dataSetB + (\a b -> + Tuple.first a == Tuple.first b + ) == + [ ( Just ( 2, "b" ), ( 2, "B" ) ) + , ( Nothing, ( 3, "C" ) ) + ] + +-} rightJoin : List b -> (a -> b -> Bool) -> List a -> List ( Maybe a, b ) rightJoin listB onPredicate listA = listB From c8878cf67bbbdaaf42d76e84216513420ca52f05 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Mon, 27 Jul 2020 17:41:01 -0400 Subject: [PATCH 2/9] Implement the List module in the SDK. #105 --- src/Morphir/SDK/List.elm | 454 ++++++++++++++++++--------------------- 1 file changed, 213 insertions(+), 241 deletions(-) diff --git a/src/Morphir/SDK/List.elm b/src/Morphir/SDK/List.elm index d4d77e3d0..a364cf066 100644 --- a/src/Morphir/SDK/List.elm +++ b/src/Morphir/SDK/List.elm @@ -60,8 +60,11 @@ with them! {-| Create a list with only one element: -singleton 1234 == [1234] -singleton "hi" == ["hi"] + + singleton 1234 == [ 1234 ] + + singleton "hi" == [ "hi" ] + -} singleton : a -> List a singleton value = @@ -69,7 +72,9 @@ singleton value = {-| Create a list with _n_ copies of a value: -repeat 3 (0,0) == [(0,0),(0,0),(0,0)] + + repeat 3 ( 0, 0 ) == [ ( 0, 0 ), ( 0, 0 ), ( 0, 0 ) ] + -} repeat : Int -> a -> List a repeat n value = @@ -78,9 +83,13 @@ repeat n value = {-| Create a list of numbers, every element increasing by one. You give the lowest and highest number that should be in the list. -range 3 6 == [3, 4, 5, 6] -range 3 3 == [3] -range 6 3 == [] + + range 3 6 == [ 3, 4, 5, 6 ] + + range 3 3 == [ 3 ] + + range 6 3 == [] + -} range : Int -> Int -> List Int range lo hi = @@ -88,10 +97,14 @@ range lo hi = {-| Add an element to the front of a list. -1 :: [2,3] == [1,2,3] -1 :: [] == [1] + + 1 :: [ 2, 3 ] == [ 1, 2, 3 ] + + 1 :: [] == [ 1 ] + This operator is pronounced _cons_ for historical reasons, but you can think of it like pushing an entry onto a stack. + -} cons : a -> List a -> List a cons = @@ -103,9 +116,13 @@ cons = {-| Apply a function to every element of a list. -map sqrt [1,4,9] == [1,2,3] -map not [True,False,True] == [False,True,False] + + map sqrt [ 1, 4, 9 ] == [ 1, 2, 3 ] + + map not [ True, False, True ] == [ False, True, False ] + So `map func [ a, b, c ]` is the same as `[ func a, func b, func c ]` + -} map : (a -> b) -> List a -> List b map f xs = @@ -114,7 +131,9 @@ map f xs = {-| Same as `map` but the function is also applied to the index of each element (starting at zero). -indexedMap Tuple.pair ["Tom","Sue","Bob"] == [ (0,"Tom"), (1,"Sue"), (2,"Bob") ] + + indexedMap Tuple.pair [ "Tom", "Sue", "Bob" ] == [ ( 0, "Tom" ), ( 1, "Sue" ), ( 2, "Bob" ) ] + -} indexedMap : (Int -> a -> b) -> List a -> List b indexedMap f xs = @@ -122,13 +141,18 @@ indexedMap f xs = {-| Reduce a list from the left. -foldl (+) 0 [1,2,3] == 6 -foldl (::) [][1,2,3] == [3,2,1] + + foldl (+) 0 [ 1, 2, 3 ] == 6 + + foldl (::) [] [ 1, 2, 3 ] == [ 3, 2, 1 ] + So `foldl step state [1,2,3]` is like saying: -state -|> step 1 -|> step 2 -|> step 3 + + state + |> step 1 + |> step 2 + |> step 3 + -} foldl : (a -> b -> b) -> b -> List a -> b foldl func acc list = @@ -136,13 +160,18 @@ foldl func acc list = {-| Reduce a list from the right. -foldr (+) 0 [1,2,3] == 6 -foldr (::) [][1,2,3] == [1,2,3] + + foldr (+) 0 [ 1, 2, 3 ] == 6 + + foldr (::) [] [ 1, 2, 3 ] == [ 1, 2, 3 ] + So `foldr step state [1,2,3]` is like saying: -state -|> step 3 -|> step 2 -|> step 1 + + state + |> step 3 + |> step 2 + |> step 1 + -} foldr : (a -> b -> b) -> b -> List a -> b foldr fn acc ls = @@ -150,7 +179,9 @@ foldr fn acc ls = {-| Keep elements that satisfy the test. -filter isEven [1,2,3,4,5,6] == [2,4,6] + + filter isEven [ 1, 2, 3, 4, 5, 6 ] == [ 2, 4, 6 ] + -} filter : (a -> Bool) -> List a -> List a filter isGood list = @@ -159,131 +190,134 @@ filter isGood list = {-| Filter out certain values. For example, maybe you have a bunch of strings from an untrusted source and you want to turn them into numbers: -numbers : List Int -numbers = -filterMap String.toInt ["3", "hi", "12", "4th", "May"] --- numbers == [3, 12] + + + numbers : List Int + numbers = + filterMap String.toInt [ "3", "hi", "12", "4th", "May" ] + + -- numbers == [3, 12] + -} filterMap : (a -> Maybe b) -> List a -> List b filterMap f xs = List.filterMap f xs -maybeCons : (a -> Maybe b) -> a -> List b -> List b -maybeCons f mx xs = - case f mx of - Just x -> - cons x xs - - Nothing -> - xs - - -- UTILITIES {-| Determine the length of a list. -length [1,2,3] == 3 + + length [ 1, 2, 3 ] == 3 + -} length : List a -> Int length xs = - foldl (\_ i -> i + 1) 0 xs + List.length xs {-| Reverse a list. -reverse [1,2,3,4] == [4,3,2,1] + + reverse [ 1, 2, 3, 4 ] == [ 4, 3, 2, 1 ] + -} reverse : List a -> List a reverse list = - foldl cons [] list + List.reverse list {-| Figure out whether a list contains a value. -member 9 [1,2,3,4] == False -member 4 [1,2,3,4] == True + + member 9 [ 1, 2, 3, 4 ] == False + + member 4 [ 1, 2, 3, 4 ] == True + -} member : a -> List a -> Bool member x xs = - any (\a -> a == x) xs + List.member x xs {-| Determine if all elements satisfy some test. -all isEven [2,4] == True -all isEven [2,3] == False -all isEven [] == True + + all isEven [ 2, 4 ] == True + + all isEven [ 2, 3 ] == False + + all isEven [] == True + -} all : (a -> Bool) -> List a -> Bool all isOkay list = - not (any (not << isOkay) list) + List.all isOkay list {-| Determine if any elements satisfy some test. -any isEven [2,3] == True -any isEven [1,3] == False -any isEven [] == False + + any isEven [ 2, 3 ] == True + + any isEven [ 1, 3 ] == False + + any isEven [] == False + -} any : (a -> Bool) -> List a -> Bool any isOkay list = - case list of - [] -> - False + List.any isOkay list + - x :: xs -> - -- note: (isOkay x || any isOkay xs) would not get TCO - if isOkay x then - True +{-| Find the maximum element in a non-empty list. - else - any isOkay xs + maximum [ 1, 4, 2 ] == Just 4 + maximum [] == Nothing -{-| Find the maximum element in a non-empty list. -maximum [1,4,2] == Just 4 -maximum [] == Nothing -} maximum : List comparable -> Maybe comparable maximum list = - case list of - x :: xs -> - Just (foldl max x xs) - - _ -> - Nothing + List.maximum list {-| Find the minimum element in a non-empty list. -minimum [3,2,1] == Just 1 -minimum [] == Nothing + + minimum [ 3, 2, 1 ] == Just 1 + + minimum [] == Nothing + -} minimum : List comparable -> Maybe comparable minimum list = - case list of - x :: xs -> - Just (foldl min x xs) - - _ -> - Nothing + List.minimum list {-| Get the sum of the list elements. -sum [1,2,3] == 6 -sum [1,1,1] == 3 -sum [] == 0 + + sum [ 1, 2, 3 ] == 6 + + sum [ 1, 1, 1 ] == 3 + + sum [] == 0 + -} sum : List number -> number sum numbers = - foldl (+) 0 numbers + List.sum numbers {-| Get the product of the list elements. -product [2,2,2] == 8 -product [3,3,3] == 27 -product [] == 1 + + product [ 2, 2, 2 ] == 8 + + product [ 3, 3, 3 ] == 27 + + product [] == 1 + -} product : List number -> number product numbers = - foldl (*) 1 numbers + List.product numbers @@ -291,89 +325,87 @@ product numbers = {-| Put two lists together. -append [1,1,2][3,5,8] == [1,1,2,3,5,8] -append ['a','b']['c'] == ['a','b','c'] + + append [ 1, 1, 2 ] [ 3, 5, 8 ] == [ 1, 1, 2, 3, 5, 8 ] + + append [ 'a', 'b' ] [ 'c' ] == [ 'a', 'b', 'c' ] + You can also use [the `(++)` operator](Basics#++) to append lists. + -} append : List a -> List a -> List a append xs ys = - case ys of - [] -> - xs - - _ -> - foldr cons ys xs + List.append xs ys {-| Concatenate a bunch of lists into a single list: -concat [[1,2],[3],[4,5]] == [1,2,3,4,5] + + concat [ [ 1, 2 ], [ 3 ], [ 4, 5 ] ] == [ 1, 2, 3, 4, 5 ] + -} concat : List (List a) -> List a concat lists = - foldr append [] lists + List.concat lists {-| Map a given function onto a list and flatten the resulting lists. -concatMap f xs == concat (map f xs) + + concatMap f xs == concat (map f xs) + -} concatMap : (a -> List b) -> List a -> List b concatMap f list = - concat (map f list) + List.concatMap f list {-| Places the given value between all members of the given list. -intersperse "on" ["turtles","turtles","turtles"] == ["turtles","on","turtles","on","turtles"] + + intersperse "on" [ "turtles", "turtles", "turtles" ] == [ "turtles", "on", "turtles", "on", "turtles" ] + -} intersperse : a -> List a -> List a intersperse sep xs = - case xs of - [] -> - [] - - hd :: tl -> - let - step x rest = - cons sep (cons x rest) - - spersed = - foldr step [] tl - in - cons hd spersed + List.intersperse sep xs {-| Combine two lists, combining them with the given function. If one list is longer, the extra elements are dropped. -totals : List Int -> List Int -> List Int -totals xs ys = -List.map2 (+) xs ys --- totals [1,2,3][4,5,6] == [5,7,9] -pairs : List a -> List b -> List (a,b) -pairs xs ys = -List.map2 Tuple.pair xs ys --- pairs ["alice","bob","chuck"][2,5,7,8] --- == [("alice",2),("bob",5),("chuck",7)] + + + totals : List Int -> List Int -> List Int + totals xs ys = + List.map2 (+) xs ys + + -- totals [1,2,3][4,5,6] == [5,7,9] + pairs : List a -> List b -> List ( a, b ) + pairs xs ys = + List.map2 Tuple.pair xs ys + + -- pairs ["alice","bob","chuck"][2,5,7,8] + -- == [("alice",2),("bob",5),("chuck",7)] + -} map2 : (a -> b -> result) -> List a -> List b -> List result map2 = - Elm.Kernel.List.map2 + List.map2 {-| -} map3 : (a -> b -> c -> result) -> List a -> List b -> List c -> List result map3 = - Elm.Kernel.List.map3 + List.map3 {-| -} map4 : (a -> b -> c -> d -> result) -> List a -> List b -> List c -> List d -> List result map4 = - Elm.Kernel.List.map4 + List.map4 {-| -} map5 : (a -> b -> c -> d -> e -> result) -> List a -> List b -> List c -> List d -> List e -> List result map5 = - Elm.Kernel.List.map5 + List.map5 @@ -381,39 +413,47 @@ map5 = {-| Sort values from lowest to highest -sort [3,1,5] == [1,3,5] + + sort [ 3, 1, 5 ] == [ 1, 3, 5 ] + -} sort : List comparable -> List comparable sort xs = - sortBy identity xs + List.sort xs {-| Sort values by a derived property. -alice = { name="Alice", height=1.62 } -bob = { name="Bob" , height=1.85 } -chuck = { name="Chuck", height=1.76 } -sortBy .name [chuck,alice,bob] == [alice,bob,chuck] -sortBy .height [chuck,alice,bob] == [alice,chuck,bob] -sortBy String.length ["mouse","cat"] == ["cat","mouse"] + + alice = { name="Alice", height=1.62 } + bob = { name="Bob" , height=1.85 } + chuck = { name="Chuck", height=1.76 } + sortBy .name [chuck,alice,bob] == [alice,bob,chuck] + sortBy .height [chuck,alice,bob] == [alice,chuck,bob] + sortBy String.length ["mouse","cat"] == ["cat","mouse"] + -} sortBy : (a -> comparable) -> List a -> List a sortBy = - Elm.Kernel.List.sortBy + List.sortBy {-| Sort values with a custom comparison function. -sortWith flippedComparison [1,2,3,4,5] == [5,4,3,2,1] -flippedComparison a b = -case compare a b of -LT -> GT -EQ -> EQ -GT -> LT + + sortWith flippedComparison [1,2,3,4,5] == [5,4,3,2,1] + + flippedComparison a b = + case compare a b of + LT -> GT + EQ -> EQ + GT -> LT + This is also the most general sort function, allowing you to define any other: `sort == sortWith compare` + -} sortWith : (a -> a -> Order) -> List a -> List a sortWith = - Elm.Kernel.List.sortWith + List.sortWith @@ -421,142 +461,78 @@ sortWith = {-| Determine if a list is empty. -isEmpty [] == True + + isEmpty [] == True + **Note:** It is usually preferable to use a `case` to test this so you do not forget to handle the `(x :: xs)` case as well! + -} isEmpty : List a -> Bool isEmpty xs = - case xs of - [] -> - True - - _ -> - False + List.isEmpty xs {-| Extract the first element of a list. -head [1,2,3] == Just 1 -head [] == Nothing + + head [ 1, 2, 3 ] == Just 1 + + head [] == Nothing + **Note:** It is usually preferable to use a `case` to deconstruct a `List` because it gives you `(x :: xs)` and you can work with both subparts. + -} head : List a -> Maybe a head list = - case list of - x :: xs -> - Just x - - [] -> - Nothing + List.head list {-| Extract the rest of the list. -tail [1,2,3] == Just [2,3] -tail [] == Nothing + + tail [ 1, 2, 3 ] == Just [ 2, 3 ] + + tail [] == Nothing + **Note:** It is usually preferable to use a `case` to deconstruct a `List` because it gives you `(x :: xs)` and you can work with both subparts. + -} tail : List a -> Maybe (List a) tail list = - case list of - x :: xs -> - Just xs - - [] -> - Nothing + List.tail list {-| Take the first _n_ members of a list. -take 2 [1,2,3,4] == [1,2] + + take 2 [ 1, 2, 3, 4 ] == [ 1, 2 ] + -} take : Int -> List a -> List a take n list = - takeFast 0 n list - - -takeFast : Int -> Int -> List a -> List a -takeFast ctr n list = - if n <= 0 then - [] - - else - case ( n, list ) of - ( _, [] ) -> - list - - ( 1, x :: _ ) -> - [ x ] - - ( 2, x :: y :: _ ) -> - [ x, y ] + List.take n list - ( 3, x :: y :: z :: _ ) -> - [ x, y, z ] - ( _, x :: y :: z :: w :: tl ) -> - if ctr > 1000 then - cons x (cons y (cons z (cons w (takeTailRec (n - 4) tl)))) - - else - cons x (cons y (cons z (cons w (takeFast (ctr + 1) (n - 4) tl)))) - - _ -> - list - - -takeTailRec : Int -> List a -> List a -takeTailRec n list = - reverse (takeReverse n list []) - - -takeReverse : Int -> List a -> List a -> List a -takeReverse n list kept = - if n <= 0 then - kept - - else - case list of - [] -> - kept - - x :: xs -> - takeReverse (n - 1) xs (cons x kept) +{-| Drop the first _n_ members of a list. + drop 2 [ 1, 2, 3, 4 ] == [ 3, 4 ] -{-| Drop the first _n_ members of a list. -drop 2 [1,2,3,4] == [3,4] -} drop : Int -> List a -> List a drop n list = - if n <= 0 then - list - - else - case list of - [] -> - list - - x :: xs -> - drop (n - 1) xs + List.drop n list {-| Partition a list based on some test. The first list contains all values that satisfy the test, and the second list contains all the value that do not. -partition (\\x -> x < 3) [0,1,2,3,4,5] == ([0,1,2], [3,4,5]) -partition isEven [0,1,2,3,4,5] == ([0,2,4], [1,3,5]) + + partition (\\x -> x < 3) [0,1,2,3,4,5] == ([0,1,2], [3,4,5]) + partition isEven [0,1,2,3,4,5] == ([0,2,4], [1,3,5]) + -} partition : (a -> Bool) -> List a -> ( List a, List a ) partition pred list = - let - step x ( trues, falses ) = - if pred x then - ( cons x trues, falses ) - - else - ( trues, cons x falses ) - in - foldr step ( [], [] ) list + List.partition pred list {-| Decompose a list of tuples into a tuple of lists. @@ -566,11 +542,7 @@ partition pred list = -} unzip : List ( a, b ) -> ( List a, List b ) unzip pairs = - let - step ( x, y ) ( xs, ys ) = - ( cons x xs, cons y ys ) - in - foldr step ( [], [] ) pairs + List.unzip pairs {-| Returns the last element of a list. From 446b61d703d50f6c2bf9a4e685af1d972e07cd44 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Thu, 30 Jul 2020 13:28:24 -0400 Subject: [PATCH 3/9] Enhancements on Scala backend. Some changes in the IR. #105 --- cli/elm.json | 2 +- cli/src/Morphir/Elm/CLI.elm | 2 +- elm.json | 1 + examples/morphir.out.json | Bin 0 -> 236986 bytes src/Morphir/Elm/Frontend.elm | 97 +++++--- src/Morphir/IR/Module/Codec.elm | 11 +- src/Morphir/IR/Name.elm | 3 + src/Morphir/IR/Package.elm | 12 +- src/Morphir/IR/Value.elm | 88 ++----- src/Morphir/IR/Value/Codec.elm | 49 ++-- src/Morphir/ListOfResults.elm | 2 + src/Morphir/Scala/Backend.elm | 367 ++++++++++++++-------------- src/Morphir/Scala/Backend/Codec.elm | 3 +- src/Morphir/Scala/PrettyPrinter.elm | 2 +- 14 files changed, 317 insertions(+), 322 deletions(-) create mode 100644 examples/morphir.out.json diff --git a/cli/elm.json b/cli/elm.json index a160011d8..074d6ae86 100644 --- a/cli/elm.json +++ b/cli/elm.json @@ -14,6 +14,7 @@ "elm/parser": "1.1.0", "elm/regex": "1.0.0", "elm-community/graph": "6.0.0", + "elm-community/list-extra": "8.2.3", "elm-community/maybe-extra": "5.2.0", "elm-explorations/test": "1.2.2", "stil4m/elm-syntax": "7.1.1" @@ -26,7 +27,6 @@ "elm/virtual-dom": "1.0.2", "elm-community/intdict": "3.0.0", "elm-community/json-extra": "4.2.0", - "elm-community/list-extra": "8.2.3", "rtfeldman/elm-hex": "1.0.0", "rtfeldman/elm-iso8601-date-strings": "1.1.3", "stil4m/structured-writer": "1.0.2" diff --git a/cli/src/Morphir/Elm/CLI.elm b/cli/src/Morphir/Elm/CLI.elm index d0f7ff9a3..e8baba86a 100644 --- a/cli/src/Morphir/Elm/CLI.elm +++ b/cli/src/Morphir/Elm/CLI.elm @@ -67,7 +67,7 @@ update msg model = Ok ( options, packageDef ) -> let fileMap = - Backend.mapPackageDefinition options packageDef + Backend.mapPackageDefinition options [ [ "morphir" ] ] packageDef in ( model, fileMap |> Ok |> encodeResult Encode.string encodeFileMap |> generateResult ) diff --git a/elm.json b/elm.json index 65bd2e7ee..d4055414b 100644 --- a/elm.json +++ b/elm.json @@ -25,6 +25,7 @@ "elm/parser": "1.1.0 <= v < 2.0.0", "elm/regex": "1.0.0 <= v < 2.0.0", "elm-community/graph": "6.0.0 <= v < 7.0.0", + "elm-community/list-extra": "8.2.4 <= v < 9.0.0", "elm-explorations/test": "1.2.2 <= v < 2.0.0", "stil4m/elm-syntax": "7.1.1 <= v < 8.0.0" }, diff --git a/examples/morphir.out.json b/examples/morphir.out.json new file mode 100644 index 0000000000000000000000000000000000000000..fddb308cc8cf5828a1998c72eff5ba6f763093c5 GIT binary patch literal 236986 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 literal 0 HcmV?d00001 diff --git a/src/Morphir/Elm/Frontend.elm b/src/Morphir/Elm/Frontend.elm index 2cc867f94..38538c59d 100644 --- a/src/Morphir/Elm/Frontend.elm +++ b/src/Morphir/Elm/Frontend.elm @@ -147,6 +147,7 @@ type Error | NotSupported SourceLocation String | DuplicateNameInPattern Name SourceLocation SourceLocation | VariableShadowing Name SourceLocation SourceLocation + | MissingTypeSignature SourceLocation encodeDeadEnd : DeadEnd -> Encode.Value @@ -200,6 +201,11 @@ encodeError error = , encodeSourceLocation sourceLocation2 ] + MissingTypeSignature sourceLocation -> + JsonExtra.encodeConstructor "MissingTypeSignature" + [ encodeSourceLocation sourceLocation + ] + type alias Imports = { lookupByExposedCtor : String -> Maybe Import @@ -389,7 +395,7 @@ mapProcessedFile currentPackagePath processedFile modulesSoFar = valuesResult : Result Errors (Dict Name (AccessControlled (Value.Definition SourceLocation))) valuesResult = - mapDeclarationsToValue processedFile.parsedFile.sourceFile moduleExpose (processedFile.file.declarations |> List.map Node.value) + mapDeclarationsToValue processedFile.parsedFile.sourceFile moduleExpose processedFile.file.declarations |> Result.map Dict.fromList moduleResult : Result Errors (Module.Definition SourceLocation) @@ -573,11 +579,11 @@ mapDeclarationsToType sourceFile expose decls = |> Result.mapError List.concat -mapDeclarationsToValue : SourceFile -> Exposing -> List Declaration -> Result Errors (List ( Name, AccessControlled (Value.Definition SourceLocation) )) +mapDeclarationsToValue : SourceFile -> Exposing -> List (Node Declaration) -> Result Errors (List ( Name, AccessControlled (Value.Definition SourceLocation) )) mapDeclarationsToValue sourceFile expose decls = decls |> List.filterMap - (\decl -> + (\(Node range decl) -> case decl of FunctionDeclaration function -> let @@ -591,7 +597,7 @@ mapDeclarationsToValue sourceFile expose decls = valueDef : Result Errors (AccessControlled (Value.Definition SourceLocation)) valueDef = - function + Node range function |> mapFunction sourceFile |> Result.map public in @@ -665,38 +671,55 @@ mapTypeAnnotation sourceFile (Node range typeAnnotation) = (mapTypeAnnotation sourceFile returnTypeNode) -mapFunction : SourceFile -> Function -> Result Errors (Value.Definition SourceLocation) -mapFunction sourceFile function = - function.declaration - |> Node.value - |> (\funImpl -> - mapFunctionImplementation sourceFile funImpl.arguments funImpl.expression - ) +mapFunction : SourceFile -> Node Function -> Result Errors (Value.Definition SourceLocation) +mapFunction sourceFile (Node range function) = + let + valueTypeResult : Result Errors (Type SourceLocation) + valueTypeResult = + case function.signature of + Just (Node _ signature) -> + mapTypeAnnotation sourceFile signature.typeAnnotation + + Nothing -> + Err [ MissingTypeSignature (SourceLocation sourceFile range) ] + in + valueTypeResult + |> Result.andThen + (\valueType -> + function.declaration + |> Node.value + |> (\funImpl -> + mapFunctionImplementation sourceFile valueType funImpl.arguments funImpl.expression + ) + ) -mapFunctionImplementation : SourceFile -> List (Node Pattern) -> Node Expression -> Result Errors (Value.Definition SourceLocation) -mapFunctionImplementation sourceFile argumentNodes expression = +mapFunctionImplementation : SourceFile -> Type SourceLocation -> List (Node Pattern) -> Node Expression -> Result Errors (Value.Definition SourceLocation) +mapFunctionImplementation sourceFile valueType argumentNodes expression = let sourceLocation : Range -> SourceLocation sourceLocation range = range |> SourceLocation sourceFile - extractNamedParams : List ( Name, SourceLocation ) -> List (Node Pattern) -> ( List ( Name, SourceLocation ), List (Node Pattern) ) - extractNamedParams namedParams patternParams = - case patternParams of - [] -> - ( namedParams, patternParams ) + extractNamedParams : List ( Name, SourceLocation, Type SourceLocation ) -> List (Node Pattern) -> Type SourceLocation -> ( List ( Name, SourceLocation, Type SourceLocation ), Type SourceLocation, List (Node Pattern) ) + extractNamedParams namedParams patternParams restOfTypeSignature = + case ( patternParams, restOfTypeSignature ) of + ( [], _ ) -> + ( namedParams, restOfTypeSignature, patternParams ) - (Node range firstParam) :: restOfParams -> + ( (Node range firstParam) :: restOfParams, Type.Function _ inType outType ) -> case firstParam of VarPattern paramName -> - extractNamedParams (namedParams ++ [ ( Name.fromString paramName, range |> SourceLocation sourceFile ) ]) restOfParams + extractNamedParams (namedParams ++ [ ( Name.fromString paramName, range |> SourceLocation sourceFile, inType ) ]) restOfParams outType _ -> - ( namedParams, patternParams ) + ( namedParams, restOfTypeSignature, patternParams ) + + _ -> + ( namedParams, restOfTypeSignature, patternParams ) - ( paramNames, lambdaArgPatterns ) = - extractNamedParams [] argumentNodes + ( inputTypes, outputType, lambdaArgPatterns ) = + extractNamedParams [] argumentNodes valueType bodyResult : Result Errors (Value.Value SourceLocation) bodyResult = @@ -715,7 +738,7 @@ mapFunctionImplementation sourceFile argumentNodes expression = lambdaWithParams lambdaArgPatterns expression in bodyResult - |> Result.map (Value.Definition Nothing paramNames) + |> Result.map (Value.Definition inputTypes outputType) mapExpression : SourceFile -> Node Expression -> Result Errors (Value.Value SourceLocation) @@ -1196,13 +1219,13 @@ mapLetExpression sourceFile sourceLocation letBlock = letDeclarationToValue : Node Expression.LetDeclaration -> Result Errors (Value.Value SourceLocation) -> Result Errors (Value.Value SourceLocation) letDeclarationToValue letDeclarationNode valueResult = - case letDeclarationNode |> Node.value of - Expression.LetFunction function -> + case letDeclarationNode of + Node range (Expression.LetFunction function) -> Result.map2 (Value.LetDefinition sourceLocation (function.declaration |> Node.value |> .name |> Node.value |> Name.fromString)) - (mapFunction sourceFile function) + (mapFunction sourceFile (Node range function)) valueResult - Expression.LetDestructuring patternNode letExpressionNode -> + Node range (Expression.LetDestructuring patternNode letExpressionNode) -> Result.map3 (Value.Destructure sourceLocation) (mapPattern sourceFile patternNode) (mapExpression sourceFile letExpressionNode) @@ -1226,12 +1249,12 @@ mapLetExpression sourceFile sourceLocation letBlock = |> Graph.nodes |> List.map (\graphNode -> - case graphNode.label |> Node.value of - Expression.LetFunction function -> - mapFunction sourceFile function + case graphNode.label of + Node range (Expression.LetFunction function) -> + mapFunction sourceFile (Node range function) |> Result.map (Tuple.pair (function.declaration |> Node.value |> .name |> Node.value |> Name.fromString)) - Expression.LetDestructuring _ _ -> + Node range (Expression.LetDestructuring _ _) -> Err [ NotSupported sourceLocation "Recursive destructuring" ] ) |> ListOfResults.liftAllErrors @@ -1349,7 +1372,9 @@ resolveLocalNames moduleResolver moduleDef = let variables : Dict Name SourceLocation variables = - valueDef.value.arguments |> Dict.fromList + valueDef.value.inputTypes + |> List.map (\( name, loc, _ ) -> ( name, loc )) + |> Dict.fromList in valueDef.value |> Value.mapDefinition rewriteTypes (rewriteValues variables) @@ -1485,7 +1510,8 @@ resolveVariablesAndReferences variables moduleResolver value = Value.LetDefinition sourceLocation name def inValue -> Result.map2 (Value.LetDefinition sourceLocation name) - (def.arguments + (def.inputTypes + |> List.map (\( argName, loc, _ ) -> ( argName, loc )) |> Dict.fromList |> Dict.insert name sourceLocation |> unionVariableNames variables @@ -1518,7 +1544,8 @@ resolveVariablesAndReferences variables moduleResolver value = |> Dict.toList |> List.map (\( name, def ) -> - def.arguments + def.inputTypes + |> List.map (\( argName, loc, _ ) -> ( argName, loc )) |> Dict.fromList |> unionVariableNames variablesAndDefNames |> Result.andThen diff --git a/src/Morphir/IR/Module/Codec.elm b/src/Morphir/IR/Module/Codec.elm index 5d69ce064..9ba5f81f2 100644 --- a/src/Morphir/IR/Module/Codec.elm +++ b/src/Morphir/IR/Module/Codec.elm @@ -83,4 +83,13 @@ decodeDefinition decodeAttributes = ) ) ) - (Decode.field "values" (Decode.succeed Dict.empty)) + (Decode.field "values" + (Decode.map Dict.fromList + (Decode.list + (Decode.map2 Tuple.pair + (Decode.index 0 decodeName) + (Decode.index 1 (decodeAccessControlled (ValueCodec.decodeDefinition decodeAttributes))) + ) + ) + ) + ) diff --git a/src/Morphir/IR/Name.elm b/src/Morphir/IR/Name.elm index aaa6548c8..4eb4ff0ba 100644 --- a/src/Morphir/IR/Name.elm +++ b/src/Morphir/IR/Name.elm @@ -150,14 +150,17 @@ words. toHumanWords : Name -> List String toHumanWords name = let + words : List String words = toList name + join : List String -> String join abbrev = abbrev |> String.join "" |> String.toUpper + process : List String -> List String -> List String -> List String process prefix abbrev suffix = case suffix of [] -> diff --git a/src/Morphir/IR/Package.elm b/src/Morphir/IR/Package.elm index f43a632d1..e957eae3a 100644 --- a/src/Morphir/IR/Package.elm +++ b/src/Morphir/IR/Package.elm @@ -1,11 +1,14 @@ module Morphir.IR.Package exposing - ( Specification + ( Distribution(..) + , Specification , Definition, emptyDefinition , PackagePath, definitionToSpecification, eraseDefinitionAttributes, eraseSpecificationAttributes ) {-| Tools to work with packages. +@docs Distribution + @docs Specification @docs Definition, emptyDefinition @@ -20,6 +23,13 @@ import Morphir.IR.Module as Module exposing (ModulePath) import Morphir.IR.Path exposing (Path) +{-| Type that represents a package distribution. A distribution contains all the necessary information to consume a +package. +-} +type Distribution + = Library PackagePath (Definition ()) + + {-| -} type alias PackagePath = Path diff --git a/src/Morphir/IR/Value.elm b/src/Morphir/IR/Value.elm index bb130063b..85bb45a57 100644 --- a/src/Morphir/IR/Value.elm +++ b/src/Morphir/IR/Value.elm @@ -4,7 +4,7 @@ module Morphir.IR.Value exposing , mapValueAttributes , Pattern(..), wildcardPattern, asPattern, tuplePattern, recordPattern, constructorPattern, emptyListPattern, headTailPattern, literalPattern , Specification, mapSpecificationAttributes - , Definition, typedDefinition, untypedDefinition, mapDefinition, mapDefinitionAttributes + , Definition, mapDefinition, mapDefinitionAttributes ) {-| This module contains the building blocks of values in the Morphir IR. @@ -41,7 +41,7 @@ is without the actual data or logic behind it. A definition is the actual data or logic as opposed to a specification which is just the specification of those. Value definitions can be typed or untyped. Exposed values have to be typed. -@docs Definition, typedDefinition, untypedDefinition, mapDefinition, mapDefinitionAttributes +@docs Definition, mapDefinition, mapDefinitionAttributes -} @@ -50,6 +50,7 @@ import Morphir.IR.FQName exposing (FQName) import Morphir.IR.Literal exposing (Literal) import Morphir.IR.Name exposing (Name) import Morphir.IR.Type as Type exposing (Type) +import Morphir.ListOfResults as ListOfResults import String @@ -103,8 +104,8 @@ type alias Specification a = which is just the specification of those. Value definitions can be typed or untyped. Exposed values have to be typed. -} type alias Definition a = - { valueType : Maybe (Type a) - , arguments : List ( Name, a ) + { inputTypes : List ( Name, a, Type a ) + , outputType : Type a , body : Value a } @@ -126,17 +127,20 @@ type alias Definition a = {-| -} mapDefinition : (Type a -> Result e (Type a)) -> (Value a -> Result e (Value a)) -> Definition a -> Result (List e) (Definition a) mapDefinition mapType mapValue def = - Result.map2 (\t v -> Definition t def.arguments v) - (case def.valueType of - Just valueType -> - mapType valueType - |> Result.map Just - - Nothing -> - Ok Nothing + Result.map3 (\inputTypes outputType body -> Definition inputTypes outputType body) + (def.inputTypes + |> List.map + (\( name, attr, tpe ) -> + mapType tpe + |> Result.map + (\t -> + ( name, attr, t ) + ) + ) + |> ListOfResults.liftAllErrors ) - (mapValue def.body) - |> Result.mapError List.singleton + (mapType def.outputType |> Result.mapError List.singleton) + (mapValue def.body |> Result.mapError List.singleton) {-| -} @@ -274,8 +278,8 @@ mapPatternAttributes f p = mapDefinitionAttributes : (a -> b) -> Definition a -> Definition b mapDefinitionAttributes f d = Definition - (d.valueType |> Maybe.map (Type.mapTypeAttributes f)) - (d.arguments |> List.map (\( name, a ) -> ( name, f a ))) + (d.inputTypes |> List.map (\( name, attr, tpe ) -> ( name, f attr, Type.mapTypeAttributes f tpe ))) + (Type.mapTypeAttributes f d.outputType) (mapValueAttributes f d.body) @@ -767,55 +771,3 @@ since it always filters. literalPattern : a -> Literal -> Pattern a literalPattern attributes value = LiteralPattern attributes value - - -{-| Typed value or function definition. - -**Note**: Elm uses patterns instead of argument names which is flexible but makes it more -difficult to understand the model. Since most business models will actually use names which -is represented as `AsPattern WildcardPattern name` in the IR we will extract those into the -definition. This is a best-efforts process and stops when it runs into a more complex pattern. -When that happens the rest of the argument patterns will be pushed down to the body as lambda -arguments. The examples below try to visualize the process. - - myFun : Int -> Int -> { foo : Int } -> Int - myFun a b { foo } = - body - - -- the above is logically translated to the below - myFun : - Int - -> Int - -> { foo : Int } - -> Int -- the value type does not change in the process - myFun a b = - \{ foo } -> - body - --} -typedDefinition : Type a -> List ( Name, a ) -> Value a -> Definition a -typedDefinition valueType argumentNames body = - Definition (Just valueType) argumentNames body - - -{-| Untyped value or function definition. - -**Note**: Elm uses patterns instead of argument names which is flexible but makes it more -difficult to understand the model. Since most business models will actually use names which -is represented as `AsPattern WildcardPattern name` in the IR we will extract those into the -definition. This is a best-efforts process and stops when it runs into a more complex pattern. -When that happens the rest of the argument patterns will be pushed down to the body as lambda -arguments. The examples below try to visualize the process. - - myFun a b { foo } = - body - - -- the above is logically translated to the below - myFun a b = - \{ foo } -> - body - --} -untypedDefinition : List ( Name, a ) -> Value a -> Definition a -untypedDefinition argumentNames body = - Definition Nothing argumentNames body diff --git a/src/Morphir/IR/Value/Codec.elm b/src/Morphir/IR/Value/Codec.elm index 8a7ecef8f..a5d76c1b3 100644 --- a/src/Morphir/IR/Value/Codec.elm +++ b/src/Morphir/IR/Value/Codec.elm @@ -299,8 +299,8 @@ decodeValue decodeAttributes = (Decode.index 3 <| Decode.list (Decode.map2 Tuple.pair - (decodePattern decodeAttributes) - (decodeValue decodeAttributes) + (Decode.index 0 (decodePattern decodeAttributes)) + (Decode.index 1 (decodeValue decodeAttributes)) ) ) @@ -467,29 +467,34 @@ encodeSpecification encodeAttributes spec = encodeDefinition : (a -> Encode.Value) -> Definition a -> Encode.Value encodeDefinition encodeAttributes def = - Encode.list identity - [ Encode.string "definition" - , case def.valueType of - Just valueType -> - encodeType encodeAttributes valueType - - Nothing -> - Encode.null - , def.arguments - |> Encode.list - (\( name, a ) -> - Encode.list identity - [ encodeName name - , encodeAttributes a - ] - ) - , encodeValue encodeAttributes def.body + Encode.object + [ ( "inputTypes" + , def.inputTypes + |> Encode.list + (\( argName, a, argType ) -> + Encode.list identity + [ encodeName argName + , encodeAttributes a + , encodeType encodeAttributes argType + ] + ) + ) + , ( "outputType", encodeType encodeAttributes def.outputType ) + , ( "body", encodeValue encodeAttributes def.body ) ] decodeDefinition : Decode.Decoder a -> Decode.Decoder (Definition a) decodeDefinition decodeAttributes = Decode.map3 Definition - (Decode.index 1 (Decode.maybe (decodeType decodeAttributes))) - (Decode.index 2 (Decode.list (Decode.map2 Tuple.pair decodeName decodeAttributes))) - (Decode.index 3 (Decode.lazy (\_ -> decodeValue decodeAttributes))) + (Decode.field "inputTypes" + (Decode.list + (Decode.map3 (\n a t -> ( n, a, t )) + (Decode.index 0 decodeName) + (Decode.index 1 decodeAttributes) + (Decode.index 2 (decodeType decodeAttributes)) + ) + ) + ) + (Decode.field "outputType" (decodeType decodeAttributes)) + (Decode.field "body" (Decode.lazy (\_ -> decodeValue decodeAttributes))) diff --git a/src/Morphir/ListOfResults.elm b/src/Morphir/ListOfResults.elm index 6bd57f2fc..d1cb3033b 100644 --- a/src/Morphir/ListOfResults.elm +++ b/src/Morphir/ListOfResults.elm @@ -4,6 +4,7 @@ module Morphir.ListOfResults exposing (liftAllErrors, liftFirstError) liftAllErrors : List (Result e a) -> Result (List e) (List a) liftAllErrors results = let + oks : List a oks = results |> List.filterMap @@ -12,6 +13,7 @@ liftAllErrors results = |> Result.toMaybe ) + errs : List e errs = results |> List.filterMap diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index 677660082..83e8f815c 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -1,11 +1,12 @@ module Morphir.Scala.Backend exposing (..) import Dict +import List.Extra as ListExtra import Morphir.File.FileMap exposing (FileMap) import Morphir.IR.AccessControlled exposing (Access(..), AccessControlled) import Morphir.IR.FQName exposing (FQName(..)) import Morphir.IR.Module as Module -import Morphir.IR.Name as Name +import Morphir.IR.Name as Name exposing (Name) import Morphir.IR.Package as Package import Morphir.IR.Path as Path exposing (Path) import Morphir.IR.Type as Type exposing (Type) @@ -15,17 +16,23 @@ import Set exposing (Set) type alias Options = - { targetPackage : List String - } + {} -mapPackageDefinition : Options -> Package.Definition a -> FileMap -mapPackageDefinition opt packageDef = +mapDistribution : Options -> Package.Distribution -> FileMap +mapDistribution opt distro = + case distro of + Package.Library packagePath packageDef -> + mapPackageDefinition opt packagePath packageDef + + +mapPackageDefinition : Options -> Package.PackagePath -> Package.Definition a -> FileMap +mapPackageDefinition opt packagePath packageDef = packageDef.modules |> Dict.toList |> List.concatMap (\( modulePath, moduleImpl ) -> - mapModuleDefinition opt packageDef modulePath moduleImpl + mapModuleDefinition opt packagePath modulePath moduleImpl |> List.map (\compilationUnit -> let @@ -48,19 +55,29 @@ mapFQNameToTypeRef (FQName packagePath modulePath localName) = [] lastName :: reverseModulePath -> - List.append (List.append (packagePath |> List.map (Name.toCamelCase >> String.toLower)) (reverseModulePath |> List.reverse |> List.map (Name.toCamelCase >> String.toLower))) [ lastName |> Name.toTitleCase ] + List.concat + [ packagePath + |> List.map (Name.toCamelCase >> String.toLower) + , reverseModulePath + |> List.reverse + |> List.map (Name.toCamelCase >> String.toLower) + , [ lastName + |> Name.toTitleCase + ] + ] in - Scala.TypeRef scalaModulePath (localName |> Name.toTitleCase) + Scala.TypeRef + scalaModulePath + (localName + |> Name.toTitleCase + ) -mapModuleDefinition : Options -> Package.Definition a -> Path -> AccessControlled (Module.Definition a) -> List Scala.CompilationUnit -mapModuleDefinition opt packageDef modulePath accessControlledModuleDef = +mapModuleDefinition : Options -> Package.PackagePath -> Path -> AccessControlled (Module.Definition a) -> List Scala.CompilationUnit +mapModuleDefinition opt currentPackagePath currentModulePath accessControlledModuleDef = let - currentPackagePath = - [ [ "morphir" ] ] - ( scalaPackagePath, moduleName ) = - case modulePath |> List.reverse of + case currentModulePath |> List.reverse of [] -> ( [], [] ) @@ -75,120 +92,57 @@ mapModuleDefinition opt packageDef modulePath accessControlledModuleDef = (\( typeName, accessControlledDocumentedTypeDef ) -> case accessControlledDocumentedTypeDef.value.value of Type.TypeAliasDefinition typeParams typeExp -> - [] + [ Scala.TypeAlias + { alias = + typeName |> Name.toTitleCase + , typeArgs = + typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar) + , tpe = + mapType typeExp + } + ] Type.CustomTypeDefinition typeParams accessControlledCtors -> - List.map Scala.MemberTypeDecl - (List.concat - [ [ Scala.Trait - { modifiers = [ Scala.Sealed ] - , name = typeName |> Name.toTitleCase - , typeArgs = typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar) - , extends = [] - , members = [] - } - ] - , accessControlledCtors.value - |> List.map - (\(Type.Constructor ctorName ctorArgs) -> - Scala.Class - { modifiers = [ Scala.Case ] - , name = ctorName |> Name.toTitleCase - , typeArgs = typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar) - , ctorArgs = - ctorArgs - |> List.map - (\( argName, argType ) -> - { modifiers = [] - , tpe = mapType argType - , name = argName |> Name.toCamelCase - , defaultValue = Nothing - } - ) - |> List.singleton - , extends = - let - parentTraitRef = - mapFQNameToTypeRef (FQName currentPackagePath modulePath typeName) - in - if List.isEmpty typeParams then - [ parentTraitRef ] - - else - [ Scala.TypeApply parentTraitRef (typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar)) ] - } - ) - ] - ) + mapCustomTypeDefinition currentPackagePath currentModulePath typeName typeParams accessControlledCtors + ) + + functionMembers : List Scala.MemberDecl + functionMembers = + accessControlledModuleDef.value.values + |> Dict.toList + |> List.concatMap + (\( valueName, accessControlledValueDef ) -> + [ Scala.FunctionDecl + { modifiers = + case accessControlledValueDef.access of + Public -> + [] + + Private -> + [ Scala.Private Nothing ] + , name = + valueName |> Name.toCamelCase + , typeArgs = + [] + , args = + [ accessControlledValueDef.value.inputTypes + |> List.map + (\( argName, a, argType ) -> + { modifiers = [] + , tpe = mapType argType + , name = argName |> Name.toCamelCase + , defaultValue = Nothing + } + ) + ] + , returnType = + Just (mapType accessControlledValueDef.value.outputType) + , body = + Just (Scala.Tuple []) + } + ] ) - --recordTypeAliasUnits = - -- impl.typeAliases - -- |> Dict.toList - -- |> List.map - -- (\( typeName, typeDecl ) -> - -- { dirPath = modulePath |> List.map (Name.toCamelCase >> String.toLower) - -- , fileName = (typeName |> Name.toTitleCase) ++ ".scala" - -- , packageDecl = modulePath |> List.map (Name.toCamelCase >> String.toLower) - -- , imports = [] - -- , typeDecls = Types.mapRecordTypeAlias typeName typeDecl - -- } - -- ) - -- - --typeAliasUnit = - -- { dirPath = modulePath |> List.map (Name.toCamelCase >> String.toLower) - -- , fileName = "package.scala" - -- , packageDecl = packagePath |> List.map (Name.toCamelCase >> String.toLower) - -- , imports = [] - -- , typeDecls = - -- [ Scala.Object - -- { modifiers = [ Scala.Package ] - -- , name = moduleName |> Name.toCamelCase |> String.toLower - -- , members = - -- impl.typeAliases - -- |> Dict.toList - -- |> List.filterMap - -- (\( typeName, typeDecl ) -> - -- case typeDecl.exp of - -- -- Do not generate type alias for record types because they will be represented by case classes - -- T.Record _ -> - -- Nothing - -- - -- -- Do not generate type alias for native types - -- T.Constructor ( [ [ "slate", "x" ], [ "core" ], [ "native" ] ], [ "native" ] ) _ -> - -- Nothing - -- - -- _ -> - -- Just - -- (Scala.TypeAlias - -- { alias = typeName |> Name.toTitleCase - -- , typeArgs = typeDecl.params |> List.map (T.Variable >> Types.mapExp) - -- , tpe = Types.mapExp typeDecl.exp - -- } - -- ) - -- ) - -- , extends = [] - -- } - -- ] - -- } - -- - --unionTypeUnits = - -- impl.unionTypes - -- |> Dict.toList - -- |> List.map - -- (\( typeName, typeDecl ) -> - -- { dirPath = - -- modulePath |> List.map (Name.toCamelCase >> String.toLower) - -- , fileName = - -- (typeName |> Name.toTitleCase) ++ ".scala" - -- , packageDecl = - -- modulePath |> List.map (Name.toCamelCase >> String.toLower) - -- , imports = - -- [] - -- , typeDecls = - -- Types.mapUnionType modulePath typeName typeDecl - -- } - -- ) moduleUnit : Scala.CompilationUnit moduleUnit = { dirPath = scalaPackagePath @@ -196,7 +150,7 @@ mapModuleDefinition opt packageDef modulePath accessControlledModuleDef = , packageDecl = scalaPackagePath , imports = [] , typeDecls = - [ Scala.Documented (Just (String.join "" [ "Generated based on ", modulePath |> Path.toString Name.toTitleCase "." ])) <| + [ Scala.Documented (Just (String.join "" [ "Generated based on ", currentModulePath |> Path.toString Name.toTitleCase "." ])) <| Scala.Object { modifiers = case accessControlledModuleDef.access of @@ -204,72 +158,18 @@ mapModuleDefinition opt packageDef modulePath accessControlledModuleDef = [] Private -> - [ Scala.Private (opt.targetPackage |> List.reverse |> List.head) ] + [ Scala.Private + (currentPackagePath + |> ListExtra.last + |> Maybe.map (Name.toCamelCase >> String.toLower) + ) + ] , name = moduleName |> Name.toTitleCase , members = - typeMembers - - --accessControlledModuleDef.value.values - -- |> Dict.toList - -- |> List.map - -- (\( name, accessControlledValue ) -> - -- let - -- scalaName = - -- name |> Name.toCamelCase - -- - -- normalizedName = - -- if reservedValueNames |> Set.member scalaName then - -- "_" ++ scalaName - -- - -- else - -- scalaName - -- - -- ( scalaValue, scalaReturnType ) = - -- case impl.valueTypes |> Dict.get name of - -- Just valueType -> - -- let - -- valueWithTypeOrError = - -- TypeInferencer.checkPackage packageDef valueType accessControlledValue - -- in - -- ( valueWithTypeOrError |> Values.mapExp, valueType |> Types.mapExp |> Just ) - -- - -- Nothing -> - -- let - -- valueWithTypeOrError = - -- TypeInferencer.inferPackage packageDef accessControlledValue - -- - -- maybeValueType = - -- valueWithTypeOrError - -- |> A.annotation - -- |> Result.toMaybe - -- in - -- ( valueWithTypeOrError |> Values.mapExp, maybeValueType |> Maybe.map Types.mapExp ) - -- in - -- Scala.FunctionDecl - -- { modifiers = [] - -- , name = normalizedName - -- , typeArgs = - -- let - -- extractedTypeArgNames = - -- impl.valueTypes - -- |> Dict.get name - -- |> Maybe.map List.singleton - -- |> Maybe.withDefault [] - -- |> Types.extractTypeArgNames - -- in - -- extractedTypeArgNames - -- |> List.map (T.Variable >> Types.mapExp) - -- , args = [] - -- , returnType = - -- impl.valueTypes - -- |> Dict.get name - -- |> Maybe.map Types.mapExp - -- , body = - -- Just scalaValue - -- } - -- ) - , extends = [] + List.append typeMembers functionMembers + , extends = + [] } ] } @@ -277,6 +177,67 @@ mapModuleDefinition opt packageDef modulePath accessControlledModuleDef = [ moduleUnit ] +mapCustomTypeDefinition : Package.PackagePath -> Path -> Name -> List Name -> AccessControlled (Type.Constructors a) -> List Scala.MemberDecl +mapCustomTypeDefinition currentPackagePath currentModulePath typeName typeParams accessControlledCtors = + let + caseClass name args extends = + Scala.Class + { modifiers = [ Scala.Case ] + , name = name |> Name.toTitleCase + , typeArgs = typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar) + , ctorArgs = + args + |> List.map + (\( argName, argType ) -> + { modifiers = [] + , tpe = mapType argType + , name = argName |> Name.toCamelCase + , defaultValue = Nothing + } + ) + |> List.singleton + , extends = extends + } + + parentTraitRef = + mapFQNameToTypeRef (FQName currentPackagePath currentModulePath typeName) + + sealedTraitHierarchy = + List.concat + [ [ Scala.Trait + { modifiers = [ Scala.Sealed ] + , name = typeName |> Name.toTitleCase + , typeArgs = typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar) + , extends = [] + , members = [] + } + ] + , accessControlledCtors.value + |> List.map + (\(Type.Constructor ctorName ctorArgs) -> + caseClass ctorName + ctorArgs + (if List.isEmpty typeParams then + [ parentTraitRef ] + + else + [ Scala.TypeApply parentTraitRef (typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar)) ] + ) + ) + ] + in + case accessControlledCtors.value of + [ Type.Constructor ctorName ctorArgs ] -> + if ctorName == typeName then + [ Scala.MemberTypeDecl (caseClass ctorName ctorArgs []) ] + + else + sealedTraitHierarchy |> List.map Scala.MemberTypeDecl + + _ -> + sealedTraitHierarchy |> List.map Scala.MemberTypeDecl + + mapType : Type a -> Scala.Type mapType tpe = case tpe of @@ -298,10 +259,36 @@ mapType tpe = Scala.TupleType (elemTypes |> List.map mapType) Type.Record a fields -> - Scala.TypeVar "Record" + Scala.StructuralType + (fields + |> List.map + (\field -> + Scala.FunctionDecl + { modifiers = [] + , name = field.name |> Name.toCamelCase + , typeArgs = [] + , args = [] + , returnType = Just (mapType field.tpe) + , body = Nothing + } + ) + ) Type.ExtensibleRecord a argName fields -> - Scala.TypeVar "ExtensibleRecord" + Scala.StructuralType + (fields + |> List.map + (\field -> + Scala.FunctionDecl + { modifiers = [] + , name = field.name |> Name.toCamelCase + , typeArgs = [] + , args = [] + , returnType = Just (mapType field.tpe) + , body = Nothing + } + ) + ) Type.Function a argType returnType -> Scala.FunctionType (mapType argType) (mapType returnType) diff --git a/src/Morphir/Scala/Backend/Codec.elm b/src/Morphir/Scala/Backend/Codec.elm index 5ceb40259..ac6daa298 100644 --- a/src/Morphir/Scala/Backend/Codec.elm +++ b/src/Morphir/Scala/Backend/Codec.elm @@ -6,5 +6,4 @@ import Morphir.Scala.Backend exposing (Options) decodeOptions : Decode.Decoder Options decodeOptions = - Decode.map Options - (Decode.field "targetPackage" (Decode.list Decode.string)) + Decode.succeed Options diff --git a/src/Morphir/Scala/PrettyPrinter.elm b/src/Morphir/Scala/PrettyPrinter.elm index 7e3545bf7..9da3fde64 100644 --- a/src/Morphir/Scala/PrettyPrinter.elm +++ b/src/Morphir/Scala/PrettyPrinter.elm @@ -98,7 +98,7 @@ mapMemberDecl : Options -> MemberDecl -> Doc mapMemberDecl opt memberDecl = case memberDecl of TypeAlias typeAlias -> - "type " ++ typeAlias.alias ++ " = " ++ mapTypeArgs opt typeAlias.typeArgs ++ mapType opt typeAlias.tpe + "type " ++ typeAlias.alias ++ mapTypeArgs opt typeAlias.typeArgs ++ " = " ++ mapType opt typeAlias.tpe FunctionDecl decl -> let From 6b01131eddf228b0787aae5145aadbf95b607f7a Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Thu, 30 Jul 2020 17:11:58 -0400 Subject: [PATCH 4/9] Fix errors. #105 --- src/Morphir/Elm/Frontend.elm | 81 ++++++++++++++--------------- tests/Morphir/Elm/FrontendTests.elm | 52 ++++++++++++------ 2 files changed, 76 insertions(+), 57 deletions(-) diff --git a/src/Morphir/Elm/Frontend.elm b/src/Morphir/Elm/Frontend.elm index 38538c59d..9f4ebe898 100644 --- a/src/Morphir/Elm/Frontend.elm +++ b/src/Morphir/Elm/Frontend.elm @@ -1319,29 +1319,30 @@ namesBoundByPattern p = |> Set.fromList +rewriteTypes : ModuleResolver -> Type SourceLocation -> Result Errors (Type SourceLocation) +rewriteTypes moduleResolver = + Rewrite.bottomUp rewriteType + (\tpe -> + case tpe of + Type.Reference sourceLocation refFullName args -> + moduleResolver.resolveType + (refFullName |> FQName.getModulePath |> List.map Name.toTitleCase) + (refFullName |> FQName.getLocalName |> Name.toTitleCase) + |> Result.map + (\resolvedFullName -> + Type.Reference sourceLocation resolvedFullName args + ) + |> Result.mapError (ResolveError sourceLocation >> List.singleton) + |> Just + + _ -> + Nothing + ) + + resolveLocalNames : ModuleResolver -> Module.Definition SourceLocation -> Result Errors (Module.Definition SourceLocation) resolveLocalNames moduleResolver moduleDef = let - rewriteTypes : Type SourceLocation -> Result Errors (Type SourceLocation) - rewriteTypes = - Rewrite.bottomUp rewriteType - (\tpe -> - case tpe of - Type.Reference sourceLocation refFullName args -> - moduleResolver.resolveType - (refFullName |> FQName.getModulePath |> List.map Name.toTitleCase) - (refFullName |> FQName.getLocalName |> Name.toTitleCase) - |> Result.map - (\resolvedFullName -> - Type.Reference sourceLocation resolvedFullName args - ) - |> Result.mapError (ResolveError sourceLocation >> List.singleton) - |> Just - - _ -> - Nothing - ) - rewriteValues : Dict Name SourceLocation -> Value SourceLocation -> Result Errors (Value SourceLocation) rewriteValues variables value = resolveVariablesAndReferences variables moduleResolver value @@ -1353,7 +1354,7 @@ resolveLocalNames moduleResolver moduleDef = |> List.map (\( typeName, typeDef ) -> typeDef.value.value - |> Type.mapDefinition rewriteTypes + |> Type.mapDefinition (rewriteTypes moduleResolver) |> Result.map (Documented typeDef.value.doc) |> Result.map (AccessControlled typeDef.access) |> Result.map (Tuple.pair typeName) @@ -1377,7 +1378,7 @@ resolveLocalNames moduleResolver moduleDef = |> Dict.fromList in valueDef.value - |> Value.mapDefinition rewriteTypes (rewriteValues variables) + |> Value.mapDefinition (rewriteTypes moduleResolver) (rewriteValues variables) |> Result.map (AccessControlled valueDef.access) |> Result.map (Tuple.pair valueName) |> Result.mapError List.concat @@ -1480,6 +1481,20 @@ resolveVariablesAndReferences variables moduleResolver value = _ -> Ok Dict.empty + + resolveValueDefinition def variablesDefNamesAndArgs = + Result.map3 Value.Definition + (def.inputTypes + |> List.map + (\( argName, a, argType ) -> + rewriteTypes moduleResolver argType + |> Result.map (\t -> ( argName, a, t )) + ) + |> ListOfResults.liftAllErrors + |> Result.mapError List.concat + ) + (rewriteTypes moduleResolver def.outputType) + (resolveVariablesAndReferences variablesDefNamesAndArgs moduleResolver def.body) in case value of Value.Reference sourceLocation (FQName [] modulePath localName) -> @@ -1515,16 +1530,7 @@ resolveVariablesAndReferences variables moduleResolver value = |> Dict.fromList |> Dict.insert name sourceLocation |> unionVariableNames variables - |> Result.andThen - (\variablesDefNameAndArgs -> - resolveVariablesAndReferences variablesDefNameAndArgs moduleResolver def.body - |> Result.map - (\resolvedBody -> - { def - | body = resolvedBody - } - ) - ) + |> Result.andThen (resolveValueDefinition def) ) (unionVariableNames variables (Dict.singleton name sourceLocation) |> Result.andThen @@ -1550,15 +1556,8 @@ resolveVariablesAndReferences variables moduleResolver value = |> unionVariableNames variablesAndDefNames |> Result.andThen (\variablesDefNamesAndArgs -> - resolveVariablesAndReferences variablesDefNamesAndArgs moduleResolver def.body - |> Result.map - (\resolvedBody -> - ( name - , { def - | body = resolvedBody - } - ) - ) + Result.map (Tuple.pair name) + (resolveValueDefinition def variablesDefNamesAndArgs) ) ) |> ListOfResults.liftAllErrors diff --git a/tests/Morphir/Elm/FrontendTests.elm b/tests/Morphir/Elm/FrontendTests.elm index 2d5de89d3..bc81026ce 100644 --- a/tests/Morphir/Elm/FrontendTests.elm +++ b/tests/Morphir/Elm/FrontendTests.elm @@ -187,7 +187,7 @@ valueTests : Test valueTests = let packageInfo = - { name = [] + { name = [ [ "my" ] ] , exposedModules = Set.fromList [ [ [ "test" ] ] ] } @@ -196,27 +196,36 @@ valueTests = { path = "Test.elm" , content = String.join "\n" - [ "module Test exposing (..)" + [ "module My.Test exposing (..)" , "" - , "import Bar as Bar" + , "import My.Bar as Bar" , "import MyPack.Bar" , "" + , "foo : Int" , "foo = 0" , "" + , "bar : Int" , "bar = 0" , "" + , "baz : Int" , "baz = 0" , "" + , "a : Int" , "a = 1" , "" + , "b : Int" , "b = 2" , "" + , "c : Int" , "c = 3" , "" + , "d : Int" , "d = 4" , "" + , "f : Int" , "f = 5" , "" + , "testValue : a" , "testValue = " ++ sourceValue ] } @@ -248,7 +257,7 @@ valueTests = ref : String -> Value () ref name = - Reference () (fQName [] [ [ "test" ] ] [ name ]) + Reference () (fQName [ [ "my" ] ] [ [ "test" ] ] [ name ]) var : String -> Value () var name = @@ -272,8 +281,9 @@ valueTests = , checkIR "False" <| Literal () (BoolLiteral False) , checkIR "'A'" <| Literal () (CharLiteral 'A') , checkIR "foo" <| ref "foo" - , checkIR "Bar.foo" <| Reference () (fQName [] [ [ "bar" ] ] [ "foo" ]) - , checkIR "MyPack.Bar.foo" <| Reference () (fQName [] [ [ "my", "pack" ], [ "bar" ] ] [ "foo" ]) + , checkIR "Bar.foo" <| Reference () (fQName [ [ "my" ] ] [ [ "bar" ] ] [ "foo" ]) + + --, checkIR "MyPack.Bar.foo" <| Reference () (fQName [] [ [ "my", "pack" ], [ "bar" ] ] [ "foo" ]) , checkIR "foo bar" <| Apply () (ref "foo") (ref "bar") , checkIR "foo bar baz" <| Apply () (Apply () (ref "foo") (ref "bar")) (ref "baz") , checkIR "-1" <| Number.negate () () (Literal () (IntLiteral 1)) @@ -338,6 +348,7 @@ valueTests = , checkIR (String.join "\n" [ " let" + , " foo : Int -> Int" , " foo a = c" , " in" , " d" @@ -346,7 +357,7 @@ valueTests = <| LetDefinition () (Name.fromString "foo") - (Definition Nothing [ ( Name.fromString "a", () ) ] (ref "c")) + (Definition [ ( Name.fromString "a", (), Int.intType () ) ] (Int.intType ()) (ref "c")) (ref "d") , checkIR (String.join "\n" @@ -387,7 +398,9 @@ valueTests = , checkIR (String.join "\n" [ " let" + , " b : Int" , " b = c" + , " a : Int" , " a = b" , " in" , " a" @@ -396,16 +409,18 @@ valueTests = <| LetDefinition () (Name.fromString "b") - (Definition Nothing [] (ref "c")) + (Definition [] (Int.intType ()) (ref "c")) (LetDefinition () (Name.fromString "a") - (Definition Nothing [] (var "b")) + (Definition [] (Int.intType ()) (var "b")) (var "a") ) , checkIR (String.join "\n" [ " let" + , " a : Int" , " a = b" + , " b : Int" , " b = c" , " in" , " a" @@ -414,16 +429,18 @@ valueTests = <| LetDefinition () (Name.fromString "b") - (Definition Nothing [] (ref "c")) + (Definition [] (Int.intType ()) (ref "c")) (LetDefinition () (Name.fromString "a") - (Definition Nothing [] (var "b")) + (Definition [] (Int.intType ()) (var "b")) (var "a") ) , checkIR (String.join "\n" [ " let" + , " a : Int" , " a = b" + , " b : Int" , " b = a" , " in" , " a" @@ -432,16 +449,19 @@ valueTests = <| LetRecursion () (Dict.fromList - [ ( Name.fromString "b", Definition Nothing [] (var "a") ) - , ( Name.fromString "a", Definition Nothing [] (var "b") ) + [ ( Name.fromString "b", Definition [] (Int.intType ()) (var "a") ) + , ( Name.fromString "a", Definition [] (Int.intType ()) (var "b") ) ] ) (var "a") , checkIR (String.join "\n" [ " let" + , " c : Int" , " c = d" + , " a : Int" , " a = b" + , " b : Int" , " b = a" , " in" , " a" @@ -450,11 +470,11 @@ valueTests = <| LetDefinition () (Name.fromString "c") - (Definition Nothing [] (ref "d")) + (Definition [] (Int.intType ()) (ref "d")) (LetRecursion () (Dict.fromList - [ ( Name.fromString "b", Definition Nothing [] (var "a") ) - , ( Name.fromString "a", Definition Nothing [] (var "b") ) + [ ( Name.fromString "b", Definition [] (Int.intType ()) (var "a") ) + , ( Name.fromString "a", Definition [] (Int.intType ()) (var "b") ) ] ) (var "a") From 7fd3d0e7301f7933e412bed4d8985d1069203214 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Mon, 3 Aug 2020 15:23:00 -0400 Subject: [PATCH 5/9] Remove RecordPattern from the IR. #109 --- src/Morphir/Elm/Frontend.elm | 20 +++++++------------- src/Morphir/IR/Value.elm | 16 +--------------- src/Morphir/IR/Value/Codec.elm | 12 ------------ tests/Morphir/Elm/FrontendTests.elm | 1 - 4 files changed, 8 insertions(+), 41 deletions(-) diff --git a/src/Morphir/Elm/Frontend.elm b/src/Morphir/Elm/Frontend.elm index 9f4ebe898..6d69be4b2 100644 --- a/src/Morphir/Elm/Frontend.elm +++ b/src/Morphir/Elm/Frontend.elm @@ -148,6 +148,7 @@ type Error | DuplicateNameInPattern Name SourceLocation SourceLocation | VariableShadowing Name SourceLocation SourceLocation | MissingTypeSignature SourceLocation + | RecordPatternNotSupported SourceLocation encodeDeadEnd : DeadEnd -> Encode.Value @@ -206,6 +207,11 @@ encodeError error = [ encodeSourceLocation sourceLocation ] + RecordPatternNotSupported sourceLocation -> + JsonExtra.encodeConstructor "RecordPatternNotSupported" + [ encodeSourceLocation sourceLocation + ] + type alias Imports = { lookupByExposedCtor : String -> Maybe Import @@ -971,12 +977,7 @@ mapPattern sourceFile (Node range pattern) = |> Result.map (Value.TuplePattern sourceLocation) Pattern.RecordPattern fieldNameNodes -> - Ok - (Value.RecordPattern sourceLocation - (fieldNameNodes - |> List.map (Node.value >> Name.fromString) - ) - ) + Err [ RecordPatternNotSupported sourceLocation ] Pattern.UnConsPattern headNode tailNode -> Result.map2 (Value.HeadTailPattern sourceLocation) @@ -1450,13 +1451,6 @@ resolveVariablesAndReferences variables moduleResolver value = ) (Ok Dict.empty) - Value.RecordPattern sourceLocation fieldNames -> - Ok - (fieldNames - |> List.map (\fieldName -> ( fieldName, sourceLocation )) - |> Dict.fromList - ) - Value.ConstructorPattern _ _ args -> args |> List.map namesBoundInPattern diff --git a/src/Morphir/IR/Value.elm b/src/Morphir/IR/Value.elm index 85bb45a57..3103eb488 100644 --- a/src/Morphir/IR/Value.elm +++ b/src/Morphir/IR/Value.elm @@ -2,7 +2,7 @@ module Morphir.IR.Value exposing ( Value(..), literal, constructor, apply, field, fieldFunction, lambda, letDef, letDestruct, letRec, list, record, reference , tuple, variable, ifThenElse, patternMatch, update, unit , mapValueAttributes - , Pattern(..), wildcardPattern, asPattern, tuplePattern, recordPattern, constructorPattern, emptyListPattern, headTailPattern, literalPattern + , Pattern(..), wildcardPattern, asPattern, tuplePattern, constructorPattern, emptyListPattern, headTailPattern, literalPattern , Specification, mapSpecificationAttributes , Definition, mapDefinition, mapDefinitionAttributes ) @@ -83,7 +83,6 @@ type Pattern a = WildcardPattern a | AsPattern a (Pattern a) Name | TuplePattern a (List (Pattern a)) - | RecordPattern a (List Name) | ConstructorPattern a FQName (List (Pattern a)) | EmptyListPattern a | HeadTailPattern a (Pattern a) (Pattern a) @@ -255,9 +254,6 @@ mapPatternAttributes f p = TuplePattern a elementPatterns -> TuplePattern (f a) (elementPatterns |> List.map (mapPatternAttributes f)) - RecordPattern a fieldNames -> - RecordPattern (f a) fieldNames - ConstructorPattern a constructorName argumentPatterns -> ConstructorPattern (f a) constructorName (argumentPatterns |> List.map (mapPatternAttributes f)) @@ -687,16 +683,6 @@ tuplePattern attributes elementPatterns = TuplePattern attributes elementPatterns -{-| Pulls out the values of some fields from a record value. - - { foo, bar } -- RecordPattern [ ["foo"], ["bar"] ] - --} -recordPattern : a -> List Name -> Pattern a -recordPattern attributes fieldNames = - RecordPattern attributes fieldNames - - {-| Matches on a custom type's constructor. **Note**: When the custom type has a single constructor this can be used for destructuring. diff --git a/src/Morphir/IR/Value/Codec.elm b/src/Morphir/IR/Value/Codec.elm index a5d76c1b3..2b93f2e69 100644 --- a/src/Morphir/IR/Value/Codec.elm +++ b/src/Morphir/IR/Value/Codec.elm @@ -348,13 +348,6 @@ encodePattern encodeAttributes pattern = , elementPatterns |> Encode.list (encodePattern encodeAttributes) ] - RecordPattern a fieldNames -> - Encode.list identity - [ Encode.string "record_pattern" - , encodeAttributes a - , fieldNames |> Encode.list encodeName - ] - ConstructorPattern a constructorName argumentPatterns -> Encode.list identity [ Encode.string "constructor_pattern" @@ -418,11 +411,6 @@ decodePattern decodeAttributes = (Decode.index 1 decodeAttributes) (Decode.index 2 <| Decode.list lazyDecodePattern) - "record_pattern" -> - Decode.map2 RecordPattern - (Decode.index 1 decodeAttributes) - (Decode.index 2 <| Decode.list decodeName) - "constructor_pattern" -> Decode.map3 ConstructorPattern (Decode.index 1 decodeAttributes) diff --git a/tests/Morphir/Elm/FrontendTests.elm b/tests/Morphir/Elm/FrontendTests.elm index bc81026ce..4e072154b 100644 --- a/tests/Morphir/Elm/FrontendTests.elm +++ b/tests/Morphir/Elm/FrontendTests.elm @@ -303,7 +303,6 @@ valueTests = , checkIR "\\42 -> foo " <| Lambda () (LiteralPattern () (IntLiteral 42)) (ref "foo") , checkIR "\\0x20 -> foo " <| Lambda () (LiteralPattern () (IntLiteral 32)) (ref "foo") , checkIR "\\( 1, 2 ) -> foo " <| Lambda () (TuplePattern () [ LiteralPattern () (IntLiteral 1), LiteralPattern () (IntLiteral 2) ]) (ref "foo") - , checkIR "\\{ foo, bar } -> foo " <| Lambda () (RecordPattern () [ Name.fromString "foo", Name.fromString "bar" ]) (var "foo") , checkIR "\\1 :: 2 -> foo " <| Lambda () (HeadTailPattern () (LiteralPattern () (IntLiteral 1)) (LiteralPattern () (IntLiteral 2))) (ref "foo") , checkIR "\\[] -> foo " <| Lambda () (EmptyListPattern ()) (ref "foo") , checkIR "\\[ 1 ] -> foo " <| Lambda () (HeadTailPattern () (LiteralPattern () (IntLiteral 1)) (EmptyListPattern ())) (ref "foo") From 4f699490f905a261bbf9db6f8c441c1efff0fb08 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Wed, 12 Aug 2020 17:00:29 -0400 Subject: [PATCH 6/9] Various improvements and fixes. #109 --- cli/morphir-elm-gen.js | 9 +- src/Morphir/Elm/Frontend/Resolve.elm | 5 +- src/Morphir/IR/Documented.elm | 4 +- src/Morphir/Scala/AST.elm | 11 ++- src/Morphir/Scala/Backend.elm | 122 +++++++++++++++++++++++++-- src/Morphir/Scala/PrettyPrinter.elm | 28 +++--- 6 files changed, 141 insertions(+), 38 deletions(-) diff --git a/cli/morphir-elm-gen.js b/cli/morphir-elm-gen.js index ced5d366e..9b1290191 100644 --- a/cli/morphir-elm-gen.js +++ b/cli/morphir-elm-gen.js @@ -22,11 +22,12 @@ const program = new commander.Command() program .name('morphir-elm gen') .description('Generate code from Morphir IR') + .option('-i, --input ', 'Source location where the Morphir IR will be loaded from. Defaults to STDIN.') .option('-o, --output ', 'Target location where the generated code will be saved. Defaults to ./dist.', './dist') .parse(process.argv) -gen(path.resolve(program.output), { targetPackage: ["com", "foo"] }) +gen(program.input, path.resolve(program.output), {}) .then(() => { console.log("Done.") }) @@ -35,9 +36,9 @@ gen(path.resolve(program.output), { targetPackage: ["com", "foo"] }) process.exit(1) }) -async function gen(outputPath, options) { - const morphirIrJson = await getStdin() - const fileMap = await generate(options, JSON.parse(morphirIrJson)) +async function gen(input, outputPath, options) { + const morphirIrJson = input ? await readFile(path.resolve(input)) : await getStdin() + const fileMap = await generate(options, JSON.parse(morphirIrJson.toString())) const writePromises = fileMap.map(async ([[dirPath, fileName], content]) => { const fileDir = dirPath.reduce((accum, next) => path.join(accum, next), outputPath) diff --git a/src/Morphir/Elm/Frontend/Resolve.elm b/src/Morphir/Elm/Frontend/Resolve.elm index bcbfb8efc..a95428985 100644 --- a/src/Morphir/Elm/Frontend/Resolve.elm +++ b/src/Morphir/Elm/Frontend/Resolve.elm @@ -128,6 +128,7 @@ moduleMapping : Dict ModuleName ModuleName moduleMapping = Dict.fromList [ ( [ "Dict" ], [ "Morphir", "SDK", "Dict" ] ) + , ( [ "Regex" ], [ "Morphir", "SDK", "Regex" ] ) ] @@ -232,7 +233,9 @@ createPackageResolver dependencies currentPackagePath currentPackageModules = let morphirModuleName : ModuleName morphirModuleName = - moduleMapping |> Dict.get moduleName |> Maybe.withDefault moduleName + moduleMapping + |> Dict.get moduleName + |> Maybe.withDefault moduleName suppliedModulePath : Path suppliedModulePath = diff --git a/src/Morphir/IR/Documented.elm b/src/Morphir/IR/Documented.elm index 4ea03605e..cf3250cfa 100644 --- a/src/Morphir/IR/Documented.elm +++ b/src/Morphir/IR/Documented.elm @@ -13,5 +13,5 @@ type alias Documented a = map : (a -> b) -> Documented a -> Documented b -map f { doc, value } = - Documented doc (f value) +map f d = + Documented d.doc (f d.value) diff --git a/src/Morphir/Scala/AST.elm b/src/Morphir/Scala/AST.elm index 41e1c4a2d..fb560ca98 100644 --- a/src/Morphir/Scala/AST.elm +++ b/src/Morphir/Scala/AST.elm @@ -81,10 +81,8 @@ type alias ArgDecl = } -type alias ArgValue = - { name : Maybe Name - , value : Value - } +type ArgValue + = ArgValue (Maybe Name) Value type MemberDecl @@ -134,11 +132,12 @@ type Value type Pattern = WildcardMatch - | AliasMatch Name + | NamedMatch Name + | AliasedMatch Name Pattern | LiteralMatch Lit | UnapplyMatch Path Name (List Pattern) | TupleMatch (List Pattern) - | ListItemsMatch (List Pattern) + | EmptyListMatch | HeadTailMatch Pattern Pattern | CommentedPattern Pattern String diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index 83e8f815c..7345c4288 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -5,11 +5,13 @@ import List.Extra as ListExtra import Morphir.File.FileMap exposing (FileMap) import Morphir.IR.AccessControlled exposing (Access(..), AccessControlled) import Morphir.IR.FQName exposing (FQName(..)) +import Morphir.IR.Literal exposing (Literal(..)) import Morphir.IR.Module as Module import Morphir.IR.Name as Name exposing (Name) import Morphir.IR.Package as Package import Morphir.IR.Path as Path exposing (Path) import Morphir.IR.Type as Type exposing (Type) +import Morphir.IR.Value exposing (Pattern(..), Value(..)) import Morphir.Scala.AST as Scala import Morphir.Scala.PrettyPrinter as PrettyPrinter import Set exposing (Set) @@ -46,8 +48,8 @@ mapPackageDefinition opt packagePath packageDef = |> Dict.fromList -mapFQNameToTypeRef : FQName -> Scala.Type -mapFQNameToTypeRef (FQName packagePath modulePath localName) = +mapFQNameToPathAndName : FQName -> ( Scala.Path, Name ) +mapFQNameToPathAndName (FQName packagePath modulePath localName) = let scalaModulePath = case modulePath |> List.reverse of @@ -66,11 +68,18 @@ mapFQNameToTypeRef (FQName packagePath modulePath localName) = ] ] in - Scala.TypeRef - scalaModulePath - (localName - |> Name.toTitleCase - ) + ( scalaModulePath + , localName + ) + + +mapFQNameToTypeRef : FQName -> Scala.Type +mapFQNameToTypeRef fQName = + let + ( path, name ) = + mapFQNameToPathAndName fQName + in + Scala.TypeRef path (name |> Name.toTitleCase) mapModuleDefinition : Options -> Package.PackagePath -> Path -> AccessControlled (Module.Definition a) -> List Scala.CompilationUnit @@ -138,7 +147,7 @@ mapModuleDefinition opt currentPackagePath currentModulePath accessControlledMod , returnType = Just (mapType accessControlledValueDef.value.outputType) , body = - Just (Scala.Tuple []) + Just (mapValue accessControlledValueDef.value.body) } ] ) @@ -297,6 +306,103 @@ mapType tpe = Scala.TypeRef [ "scala" ] "Unit" +mapLiteral : Literal -> Scala.Lit +mapLiteral literal = + case literal of + BoolLiteral v -> + Scala.BooleanLit v + + CharLiteral v -> + Scala.CharacterLit v + + StringLiteral v -> + Scala.StringLit v + + IntLiteral v -> + Scala.IntegerLit v + + FloatLiteral v -> + Scala.FloatLit v + + +mapValue : Value a -> Scala.Value +mapValue value = + case value of + Literal a v -> + Scala.Literal (mapLiteral v) + + --| Constructor a FQName + --| Tuple a (List (Value a)) + --| List a (List (Value a)) + --| Record a (List ( Name, Value a )) + Variable a name -> + Scala.Var (name |> Name.toCamelCase) + + Reference a fQName -> + let + ( path, name ) = + mapFQNameToPathAndName fQName + in + Scala.Ref path (name |> Name.toCamelCase) + + --| Field a (Value a) Name + --| FieldFunction a Name + Apply a fun arg -> + Scala.Apply (mapValue fun) [ Scala.ArgValue Nothing (mapValue arg) ] + + --| Lambda a (Pattern a) (Value a) + --| LetDefinition a Name (Definition a) (Value a) + --| LetRecursion a (Dict Name (Definition a)) (Value a) + --| Destructure a (Pattern a) (Value a) (Value a) + --| IfThenElse a (Value a) (Value a) (Value a) + --| PatternMatch a (Value a) (List ( Pattern a, Value a )) + --| UpdateRecord a (Value a) (List ( Name, Value a )) + --| Unit a + _ -> + Scala.Tuple [] + + +mapPattern : Pattern a -> Scala.Pattern +mapPattern pattern = + case pattern of + WildcardPattern a -> + Scala.WildcardMatch + + AsPattern a (WildcardPattern _) alias -> + Scala.NamedMatch (alias |> Name.toCamelCase) + + AsPattern a aliasedPattern alias -> + Scala.AliasedMatch (alias |> Name.toCamelCase) (mapPattern aliasedPattern) + + TuplePattern a itemPatterns -> + Scala.TupleMatch (itemPatterns |> List.map mapPattern) + + ConstructorPattern a fQName argPatterns -> + let + ( path, name ) = + mapFQNameToPathAndName fQName + in + Scala.UnapplyMatch path + (name |> Name.toTitleCase) + (argPatterns + |> List.map mapPattern + ) + + EmptyListPattern a -> + Scala.EmptyListMatch + + HeadTailPattern a headPattern tailPattern -> + Scala.HeadTailMatch + (mapPattern headPattern) + (mapPattern tailPattern) + + LiteralPattern a literal -> + Scala.LiteralMatch (mapLiteral literal) + + UnitPattern a -> + Scala.WildcardMatch + + reservedValueNames : Set String reservedValueNames = Set.fromList diff --git a/src/Morphir/Scala/PrettyPrinter.elm b/src/Morphir/Scala/PrettyPrinter.elm index 9da3fde64..2ad424ace 100644 --- a/src/Morphir/Scala/PrettyPrinter.elm +++ b/src/Morphir/Scala/PrettyPrinter.elm @@ -310,7 +310,7 @@ mapValue opt value = "_" Apply funValue argValues -> - mapValue opt funValue ++ argValueBlock opt argValues + parens (mapValue opt funValue) ++ argValueBlock opt argValues UnOp op right -> op ++ mapValue opt right @@ -396,9 +396,12 @@ mapPattern pattern = WildcardMatch -> "_" - AliasMatch name -> + NamedMatch name -> name + AliasedMatch name aliasedPattern -> + concat [ name, " @ ", mapPattern aliasedPattern ] + LiteralMatch lit -> mapLit lit @@ -427,17 +430,8 @@ mapPattern pattern = |> concat ) - ListItemsMatch itemPatterns -> - let - itemsToCons patterns = - case patterns of - [] -> - "Nil" - - headPattern :: tailPatterns -> - mapPattern headPattern ++ " :: " ++ itemsToCons tailPatterns - in - itemsToCons itemPatterns + EmptyListMatch -> + "Nil" HeadTailMatch headPattern tailPattern -> mapPattern headPattern ++ " :: " ++ mapPattern tailPattern @@ -484,13 +478,13 @@ argValueBlock opt argValues = parens (argValues |> List.map - (\argValue -> - case argValue.name of + (\(ArgValue name value) -> + case name of Just argName -> - argName ++ " = " ++ mapValue opt argValue.value + argName ++ " = " ++ mapValue opt value Nothing -> - mapValue opt argValue.value + mapValue opt value ) |> List.intersperse ", " |> concat From 090e9eeab98a0455575c2b8b1d4bfdfeaed91c06 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Wed, 12 Aug 2020 17:43:59 -0400 Subject: [PATCH 7/9] Temporary and incomplete fix for associativity bug in elm-syntax. #109 --- src/Morphir/Elm/Frontend.elm | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Morphir/Elm/Frontend.elm b/src/Morphir/Elm/Frontend.elm index 45bcd18dd..4a00c0df0 100644 --- a/src/Morphir/Elm/Frontend.elm +++ b/src/Morphir/Elm/Frontend.elm @@ -25,11 +25,12 @@ import Elm.Syntax.Declaration exposing (Declaration(..)) import Elm.Syntax.Exposing as Exposing exposing (Exposing) import Elm.Syntax.Expression as Expression exposing (Expression, Function, FunctionImplementation) import Elm.Syntax.File exposing (File) +import Elm.Syntax.Infix as Infix import Elm.Syntax.Module as ElmModule import Elm.Syntax.ModuleName exposing (ModuleName) import Elm.Syntax.Node as Node exposing (Node(..)) import Elm.Syntax.Pattern as Pattern exposing (Pattern(..)) -import Elm.Syntax.Range exposing (Range) +import Elm.Syntax.Range as Range exposing (Range) import Elm.Syntax.TypeAnnotation exposing (TypeAnnotation(..)) import Graph exposing (Graph) import Json.Decode as Decode @@ -770,7 +771,7 @@ mapExpression sourceFile (Node range exp) = sourceLocation = range |> SourceLocation sourceFile in - case exp of + case fixAssociativity exp of Expression.UnitExpr -> Ok (Value.Unit sourceLocation) @@ -1677,3 +1678,21 @@ withAccessControl isExposed a = else private a + + +{-| This is an incomplete fis for an associativity issue in elm-syntax. +It only works when the operators are the same instead of relying on precedence equality. +Consequently it also doesn't take mixed associativities into account. +-} +fixAssociativity : Expression -> Expression +fixAssociativity expr = + case expr of + Expression.OperatorApplication o d (Node lr l) (Node _ (Expression.OperatorApplication ro rd (Node rlr rl) (Node rrr rr))) -> + if (o == ro) && d == Infix.Left then + Expression.OperatorApplication o d (Node (Range.combine [ lr, rlr ]) (Expression.OperatorApplication ro rd (Node lr l) (Node rlr rl))) (Node rrr rr) + + else + expr + + _ -> + expr From 31edec190269bc91e35d4003091ee11dcdf3b9c1 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Fri, 14 Aug 2020 17:52:10 -0400 Subject: [PATCH 8/9] Full coverage of values. #109 --- cli/src/Morphir/Elm/CLI.elm | 35 +-- elm.json | 2 +- src/Morphir/Elm/Frontend.elm | 11 +- src/Morphir/Elm/Frontend/Resolve.elm | 195 +++++++++--- src/Morphir/IR/Package/Codec.elm | 50 +++- src/Morphir/IR/Value.elm | 46 ++- src/Morphir/IR/Value/Codec.elm | 36 +-- src/Morphir/Scala/AST.elm | 31 +- src/Morphir/Scala/Backend.elm | 281 +++++++++++++++--- src/Morphir/Scala/PrettyPrinter.elm | 112 ++++--- tests-integration/reference-model/elm.json | 24 ++ .../reference-model/morphir.json | 8 + .../src/Morphir/Reference/Model/Types.elm | 23 ++ .../src/Morphir/Reference/Model/Values.elm | 170 +++++++++++ 14 files changed, 819 insertions(+), 205 deletions(-) create mode 100644 tests-integration/reference-model/elm.json create mode 100644 tests-integration/reference-model/morphir.json create mode 100644 tests-integration/reference-model/src/Morphir/Reference/Model/Types.elm create mode 100644 tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm diff --git a/cli/src/Morphir/Elm/CLI.elm b/cli/src/Morphir/Elm/CLI.elm index 0d370a811..b4005800d 100644 --- a/cli/src/Morphir/Elm/CLI.elm +++ b/cli/src/Morphir/Elm/CLI.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -66,25 +66,26 @@ update msg model = result = Frontend.packageDefinitionFromSource packageInfo sourceFiles |> Result.map Package.eraseDefinitionAttributes + |> Result.map (Package.Library packageInfo.name) in - ( model, result |> encodeResult (Encode.list encodeError) (PackageCodec.encodeDefinition (\_ -> Encode.object [])) |> packageDefinitionFromSourceResult ) + ( model, result |> encodeResult (Encode.list encodeError) PackageCodec.encodeDistribution |> packageDefinitionFromSourceResult ) Err errorMessage -> ( model, errorMessage |> Decode.errorToString |> decodeError ) - Generate ( optionsJson, packageDefJson ) -> + Generate ( optionsJson, packageDistJson ) -> let optionsResult = Decode.decodeValue decodeOptions optionsJson - packageDefResult = - Decode.decodeValue (PackageCodec.decodeDefinition (Decode.succeed ())) packageDefJson + packageDistroResult = + Decode.decodeValue PackageCodec.decodeDistribution packageDistJson in - case Result.map2 Tuple.pair optionsResult packageDefResult of - Ok ( options, packageDef ) -> + case Result.map2 Tuple.pair optionsResult packageDistroResult of + Ok ( options, packageDist ) -> let fileMap = - Backend.mapPackageDefinition options [ [ "morphir" ] ] packageDef + Backend.mapDistribution options packageDist in ( model, fileMap |> Ok |> encodeResult Encode.string encodeFileMap |> generateResult ) diff --git a/elm.json b/elm.json index c33ff3721..b3470a7c8 100644 --- a/elm.json +++ b/elm.json @@ -27,7 +27,7 @@ "elm-community/graph": "6.0.0 <= v < 7.0.0", "elm-community/list-extra": "8.2.4 <= v < 9.0.0", "elm-explorations/test": "1.2.2 <= v < 2.0.0", - "stil4m/elm-syntax": "7.1.1 <= v < 8.0.0" + "stil4m/elm-syntax": "7.1.3 <= v < 8.0.0" }, "test-dependencies": {} } diff --git a/src/Morphir/Elm/Frontend.elm b/src/Morphir/Elm/Frontend.elm index 4a00c0df0..a675bb417 100644 --- a/src/Morphir/Elm/Frontend.elm +++ b/src/Morphir/Elm/Frontend.elm @@ -315,7 +315,7 @@ packageDefinitionFromSource packageInfo sourceFiles = |> Morphir.Graph.topologicalSort in if Morphir.Graph.isEmpty cycles then - Ok sortedModules + Ok (sortedModules |> List.reverse) else Err [ CyclicModules cycles ] @@ -1509,6 +1509,15 @@ resolveVariablesAndReferences variables moduleResolver value = (resolveVariablesAndReferences variablesDefNamesAndArgs moduleResolver def.body) in case value of + --Value.Constructor sourceLocation (FQName [] modulePath localName) -> + -- moduleResolver.resolveCtor + -- (modulePath |> List.map Name.toTitleCase) + -- (localName |> Name.toTitleCase) + -- |> Result.map + -- (\resolvedFullName -> + -- Value.Constructor sourceLocation resolvedFullName + -- ) + -- |> Result.mapError (ResolveError sourceLocation >> List.singleton) Value.Reference sourceLocation (FQName [] modulePath localName) -> if variables |> Dict.member localName then Ok (Value.Variable sourceLocation localName) diff --git a/src/Morphir/Elm/Frontend/Resolve.elm b/src/Morphir/Elm/Frontend/Resolve.elm index f685fda21..62b209440 100644 --- a/src/Morphir/Elm/Frontend/Resolve.elm +++ b/src/Morphir/Elm/Frontend/Resolve.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -23,6 +23,7 @@ import Elm.Syntax.Import exposing (Import) import Elm.Syntax.Node as Node exposing (Node(..)) import Elm.Syntax.Range exposing (emptyRange) import Json.Encode as Encode +import Morphir.IR.AccessControlled exposing (AccessControlled) import Morphir.IR.FQName exposing (FQName, fQName) import Morphir.IR.Module as Module import Morphir.IR.Name as Name exposing (Name) @@ -43,7 +44,7 @@ type alias LocalName = type Error = CouldNotDecompose ModuleName - | CouldNotFindLocalName LocalName + | CouldNotFindLocalName ResolveTarget LocalName | CouldNotFindName Path Path Name | CouldNotFindModule Path Path | CouldNotFindPackage Path @@ -59,9 +60,11 @@ encodeError error = JsonExtra.encodeConstructor "CouldNotDecompose" [ Encode.string (String.join "." moduleName) ] - CouldNotFindLocalName localName -> + CouldNotFindLocalName target localName -> JsonExtra.encodeConstructor "CouldNotFindLocalName" - [ Encode.string localName ] + [ encodeResolveTarget target + , Encode.string localName + ] CouldNotFindName packagePath modulePath localName -> JsonExtra.encodeConstructor "CouldNotFindName" @@ -97,6 +100,7 @@ encodeError error = type alias ModuleResolver = { resolveType : ModuleName -> LocalName -> Result Error FQName + , resolveCtor : ModuleName -> LocalName -> Result Error FQName , resolveValue : ModuleName -> LocalName -> Result Error FQName } @@ -105,11 +109,31 @@ type alias PackageResolver = { packagePath : Path , ctorNames : ModuleName -> LocalName -> Result Error (List String) , exposesType : ModuleName -> LocalName -> Result Error Bool + , exposesCtor : ModuleName -> LocalName -> Result Error Bool , exposesValue : ModuleName -> LocalName -> Result Error Bool , decomposeModuleName : ModuleName -> Result Error ( Path, Path ) } +type ResolveTarget + = Type + | Ctor + | Value + + +encodeResolveTarget : ResolveTarget -> Encode.Value +encodeResolveTarget target = + case target of + Type -> + Encode.string "type" + + Ctor -> + Encode.string "ctor" + + Value -> + Encode.string "value" + + defaultImports : List Import defaultImports = let @@ -225,6 +249,43 @@ createPackageResolver dependencies currentPackagePath currentPackageModules = ) ) + exposesCtor : ModuleName -> LocalName -> Result Error Bool + exposesCtor moduleName localName = + let + ctorName : Name + ctorName = + Name.fromString localName + in + decomposeModuleName moduleName + |> Result.andThen + (\( packagePath, modulePath ) -> + lookupModule packagePath modulePath + |> Result.map + (\moduleDecl -> + let + allCtorNames : List Name + allCtorNames = + moduleDecl.types + |> Dict.toList + |> List.concatMap + (\( _, documentedTypeDecl ) -> + case documentedTypeDecl.value of + Type.CustomTypeSpecification _ ctors -> + ctors + |> List.map + (\(Type.Constructor cName _) -> + cName + ) + + _ -> + [] + ) + in + allCtorNames + |> List.member ctorName + ) + ) + exposesValue : ModuleName -> LocalName -> Result Error Bool exposesValue moduleName localName = let @@ -276,7 +337,7 @@ createPackageResolver dependencies currentPackagePath currentPackageModules = ) |> Result.fromMaybe (CouldNotDecompose morphirModuleName) in - PackageResolver currentPackagePath ctorNames exposesType exposesValue decomposeModuleName + PackageResolver currentPackagePath ctorNames exposesType exposesCtor exposesValue decomposeModuleName createModuleResolver : PackageResolver -> List Import -> Path -> Module.Definition a -> ModuleResolver @@ -345,14 +406,11 @@ createModuleResolver packageResolver elmImports currenctModulePath moduleDef = [] ) - explicitValueNames : Dict LocalName ModuleName - explicitValueNames = + explicitCtorNames : Dict LocalName ModuleName + explicitCtorNames = explicitNames (\moduleName topLevelExpose -> case topLevelExpose of - FunctionExpose name -> - [ name ] - TypeExpose { name, open } -> open |> Maybe.andThen @@ -366,6 +424,18 @@ createModuleResolver packageResolver elmImports currenctModulePath moduleDef = [] ) + explicitValueNames : Dict LocalName ModuleName + explicitValueNames = + explicitNames + (\moduleName topLevelExpose -> + case topLevelExpose of + FunctionExpose name -> + [ name ] + + _ -> + [] + ) + allExposeModules : List ModuleName allExposeModules = imports @@ -400,22 +470,30 @@ createModuleResolver packageResolver elmImports currenctModulePath moduleDef = ) |> Dict.fromList - resolveWithoutModuleName : Bool -> LocalName -> Maybe ModuleName - resolveWithoutModuleName isType localName = + resolveWithoutModuleName : ResolveTarget -> LocalName -> Maybe ModuleName + resolveWithoutModuleName target localName = let explNames = - if isType then - explicitTypeNames + case target of + Type -> + explicitTypeNames - else - explicitValueNames + Ctor -> + explicitCtorNames + + Value -> + explicitValueNames exposes = - if isType then - packageResolver.exposesType + case target of + Type -> + packageResolver.exposesType - else - packageResolver.exposesValue + Ctor -> + packageResolver.exposesCtor + + Value -> + packageResolver.exposesValue in case explNames |> Dict.get localName of Just moduleName -> @@ -434,12 +512,12 @@ createModuleResolver packageResolver elmImports currenctModulePath moduleDef = ) |> List.head - resolveModuleName : Bool -> ModuleName -> LocalName -> Result Error ModuleName - resolveModuleName isType moduleName localName = + resolveModuleName : ResolveTarget -> ModuleName -> LocalName -> Result Error ModuleName + resolveModuleName target moduleName localName = case moduleName of [] -> - resolveWithoutModuleName isType localName - |> Result.fromMaybe (CouldNotFindLocalName localName) + resolveWithoutModuleName target localName + |> Result.fromMaybe (CouldNotFindLocalName target localName) [ moduleAlias ] -> moduleAliases @@ -453,26 +531,44 @@ createModuleResolver packageResolver elmImports currenctModulePath moduleDef = else Err (ModuleNotImported fullModuleName) - resolveExternally : Bool -> ModuleName -> LocalName -> Result Error FQName - resolveExternally isType moduleName localName = - resolveModuleName isType moduleName localName + resolveExternally : ResolveTarget -> ModuleName -> LocalName -> Result Error FQName + resolveExternally target moduleName localName = + resolveModuleName target moduleName localName |> Result.andThen packageResolver.decomposeModuleName |> Result.map (\( packagePath, modulePath ) -> fQName packagePath modulePath (Name.fromString localName) ) - resolve : Bool -> ModuleName -> LocalName -> Result Error FQName - resolve isType elmModuleName elmLocalName = + resolve : ResolveTarget -> ModuleName -> LocalName -> Result Error FQName + resolve target elmModuleName elmLocalName = if List.isEmpty elmModuleName then -- If the name is not prefixed with a module we need to look it up within the module first let localNames = - if isType then - moduleDef.types |> Dict.keys + case target of + Type -> + moduleDef.types |> Dict.keys + + Ctor -> + moduleDef.types + |> Dict.toList + |> List.concatMap + (\( _, accessControlledDocumentedTypeDef ) -> + case accessControlledDocumentedTypeDef.value.value of + Type.CustomTypeDefinition _ accessControlledCtors -> + accessControlledCtors.value + |> List.map + (\(Type.Constructor name _) -> + name + ) + + _ -> + [] + ) - else - moduleDef.values |> Dict.keys + Value -> + moduleDef.values |> Dict.keys localName = elmLocalName |> Name.fromString @@ -485,20 +581,25 @@ createModuleResolver packageResolver elmImports currenctModulePath moduleDef = Err (PackageNotPrefixOfModule packageResolver.packagePath currenctModulePath) else - resolveExternally isType elmModuleName elmLocalName + resolveExternally target elmModuleName elmLocalName else -- If the name is prefixed with a module we can skip the local resolution - resolveExternally isType elmModuleName elmLocalName + resolveExternally target elmModuleName elmLocalName resolveType : ModuleName -> LocalName -> Result Error FQName resolveType moduleName = - resolve True + resolve Type + (moduleMapping |> Dict.get moduleName |> Maybe.withDefault moduleName) + + resolveCtor : ModuleName -> LocalName -> Result Error FQName + resolveCtor moduleName = + resolve Ctor (moduleMapping |> Dict.get moduleName |> Maybe.withDefault moduleName) resolveValue : ModuleName -> LocalName -> Result Error FQName resolveValue moduleName = - resolve False + resolve Value (moduleMapping |> Dict.get moduleName |> Maybe.withDefault moduleName) in - ModuleResolver resolveType resolveValue + ModuleResolver resolveType resolveCtor resolveValue diff --git a/src/Morphir/IR/Package/Codec.elm b/src/Morphir/IR/Package/Codec.elm index cd25c5ab2..7bc64a7fb 100644 --- a/src/Morphir/IR/Package/Codec.elm +++ b/src/Morphir/IR/Package/Codec.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -22,7 +22,8 @@ import Json.Decode as Decode import Json.Encode as Encode import Morphir.IR.AccessControlled.Codec exposing (decodeAccessControlled, encodeAccessControlled) import Morphir.IR.Module.Codec as ModuleCodec -import Morphir.IR.Package exposing (Definition, Specification) +import Morphir.IR.Name.Codec exposing (encodeName) +import Morphir.IR.Package exposing (Definition, Distribution(..), Specification) import Morphir.IR.Path.Codec exposing (decodePath, encodePath) @@ -87,3 +88,30 @@ decodeDefinition decodeAttributes = ) ) ) + + +encodeDistribution : Distribution -> Encode.Value +encodeDistribution distro = + case distro of + Library packagePath def -> + Encode.list identity + [ Encode.string "library" + , encodePath packagePath + , encodeDefinition (\_ -> Encode.object []) def + ] + + +decodeDistribution : Decode.Decoder Distribution +decodeDistribution = + Decode.index 0 Decode.string + |> Decode.andThen + (\kind -> + case kind of + "library" -> + Decode.map2 Library + (Decode.index 1 decodePath) + (Decode.index 2 (decodeDefinition (Decode.succeed ()))) + + other -> + Decode.fail <| "Unknown value type: " ++ other + ) diff --git a/src/Morphir/IR/Value.elm b/src/Morphir/IR/Value.elm index 65ae57e4d..8e87fb130 100644 --- a/src/Morphir/IR/Value.elm +++ b/src/Morphir/IR/Value.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -22,6 +22,7 @@ module Morphir.IR.Value exposing , Pattern(..), wildcardPattern, asPattern, tuplePattern, constructorPattern, emptyListPattern, headTailPattern, literalPattern , Specification, mapSpecificationAttributes , Definition, mapDefinition, mapDefinitionAttributes + , uncurryApply ) {-| This module contains the building blocks of values in the Morphir IR. @@ -60,6 +61,11 @@ which is just the specification of those. Value definitions can be typed or unty @docs Definition, mapDefinition, mapDefinitionAttributes + +# Utilities + +@docs uncurryApply + -} import Dict exposing (Dict) @@ -774,3 +780,23 @@ since it always filters. literalPattern : a -> Literal -> Pattern a literalPattern attributes value = LiteralPattern attributes value + + +{-| Extract the argument list from a curried apply tree. It takes the two arguments of an apply and returns a tuple of +the function and a list of arguments. + + uncurryApply (Apply () f a) b == ( f, [ a, b ] ) + +-} +uncurryApply : Value a -> Value a -> ( Value a, List (Value a) ) +uncurryApply fun lastArg = + case fun of + Apply _ nestedFun nestedArg -> + let + ( f, initArgs ) = + uncurryApply nestedFun nestedArg + in + ( f, List.append initArgs [ lastArg ] ) + + _ -> + ( fun, [ lastArg ] ) diff --git a/src/Morphir/IR/Value/Codec.elm b/src/Morphir/IR/Value/Codec.elm index 02dc54869..0236e2a16 100644 --- a/src/Morphir/IR/Value/Codec.elm +++ b/src/Morphir/IR/Value/Codec.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -177,7 +177,7 @@ encodeValue encodeAttributes v = UpdateRecord a valueToUpdate fieldsToUpdate -> Encode.list identity - [ Encode.string "update" + [ Encode.string "update_record" , encodeAttributes a , encodeValue encodeAttributes valueToUpdate , fieldsToUpdate @@ -324,12 +324,14 @@ decodeValue decodeAttributes = "update_record" -> Decode.map3 UpdateRecord (Decode.index 1 decodeAttributes) - (Decode.index 2 <| decodeValue decodeAttributes) - (Decode.index 3 <| - Decode.list <| - Decode.map2 Tuple.pair - decodeName - (decodeValue decodeAttributes) + (Decode.index 2 (decodeValue decodeAttributes)) + (Decode.index 3 + (Decode.list + (Decode.map2 Tuple.pair + (Decode.index 0 decodeName) + (Decode.index 1 (decodeValue decodeAttributes)) + ) + ) ) "unit" -> diff --git a/src/Morphir/Scala/AST.elm b/src/Morphir/Scala/AST.elm index 7eaaccb3c..3e8e8a915 100644 --- a/src/Morphir/Scala/AST.elm +++ b/src/Morphir/Scala/AST.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -108,6 +108,11 @@ type MemberDecl , typeArgs : List Type , tpe : Type } + | ValueDecl + { modifiers : List Mod + , pattern : Pattern + , value : Value + } | FunctionDecl { modifiers : List Mod , name : Name @@ -131,7 +136,7 @@ type Type type Value = Literal Lit - | Var Name + | Variable Name | Ref Path Name | Select Value Name | Wildcard @@ -139,11 +144,13 @@ type Value | UnOp String Value | BinOp Value String Value | Lambda (List Name) Value - | LetBlock (List ( Pattern, Value )) Value + | Block (List MemberDecl) Value | MatchCases (List ( Pattern, Value )) | Match Value Value | IfElse Value Value Value | Tuple (List Value) + | StructuralValue (List ( Name, Value )) + | Unit | CommentedValue Value String diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index 5169dd77e..792061144 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -28,7 +28,7 @@ import Morphir.IR.Name as Name exposing (Name) import Morphir.IR.Package as Package import Morphir.IR.Path as Path exposing (Path) import Morphir.IR.Type as Type exposing (Type) -import Morphir.IR.Value exposing (Pattern(..), Value(..)) +import Morphir.IR.Value as Value exposing (Pattern(..), Value(..)) import Morphir.Scala.AST as Scala import Morphir.Scala.PrettyPrinter as PrettyPrinter import Set exposing (Set) @@ -117,6 +117,28 @@ mapModuleDefinition opt currentPackagePath currentModulePath accessControlledMod |> List.concatMap (\( typeName, accessControlledDocumentedTypeDef ) -> case accessControlledDocumentedTypeDef.value.value of + Type.TypeAliasDefinition typeParams (Type.Record _ fields) -> + [ Scala.MemberTypeDecl + (Scala.Class + { modifiers = [ Scala.Case ] + , name = typeName |> Name.toTitleCase + , typeArgs = typeParams |> List.map (Name.toTitleCase >> Scala.TypeVar) + , ctorArgs = + fields + |> List.map + (\field -> + { modifiers = [] + , tpe = mapType field.tpe + , name = field.name |> Name.toCamelCase + , defaultValue = Nothing + } + ) + |> List.singleton + , extends = [] + } + ) + ] + Type.TypeAliasDefinition typeParams typeExp -> [ Scala.TypeAlias { alias = @@ -151,16 +173,20 @@ mapModuleDefinition opt currentPackagePath currentModulePath accessControlledMod , typeArgs = [] , args = - [ accessControlledValueDef.value.inputTypes - |> List.map - (\( argName, a, argType ) -> - { modifiers = [] - , tpe = mapType argType - , name = argName |> Name.toCamelCase - , defaultValue = Nothing - } - ) - ] + if List.isEmpty accessControlledValueDef.value.inputTypes then + [] + + else + [ accessControlledValueDef.value.inputTypes + |> List.map + (\( argName, a, argType ) -> + { modifiers = [] + , tpe = mapType argType + , name = argName |> Name.toCamelCase + , defaultValue = Nothing + } + ) + ] , returnType = Just (mapType accessControlledValueDef.value.outputType) , body = @@ -323,37 +349,64 @@ mapType tpe = Scala.TypeRef [ "scala" ] "Unit" -mapLiteral : Literal -> Scala.Lit -mapLiteral literal = - case literal of - BoolLiteral v -> - Scala.BooleanLit v +mapValue : Value a -> Scala.Value +mapValue value = + case value of + Literal a literal -> + let + wrap : String -> Scala.Lit -> Scala.Value + wrap moduleName lit = + Scala.Apply + (Scala.Ref [ "morphir", "sdk" ] moduleName) + [ Scala.ArgValue Nothing (Scala.Literal lit) ] + in + case literal of + BoolLiteral v -> + wrap "Bool" (Scala.BooleanLit v) - CharLiteral v -> - Scala.CharacterLit v + CharLiteral v -> + wrap "Char" (Scala.CharacterLit v) - StringLiteral v -> - Scala.StringLit v + StringLiteral v -> + wrap "String" (Scala.StringLit v) - IntLiteral v -> - Scala.IntegerLit v + IntLiteral v -> + wrap "Int" (Scala.IntegerLit v) - FloatLiteral v -> - Scala.FloatLit v + FloatLiteral v -> + wrap "Float" (Scala.FloatLit v) + Constructor a fQName -> + let + ( path, name ) = + mapFQNameToPathAndName fQName + in + Scala.Ref path + (name |> Name.toTitleCase) -mapValue : Value a -> Scala.Value -mapValue value = - case value of - Literal a v -> - Scala.Literal (mapLiteral v) + Tuple a elemValues -> + Scala.Tuple + (elemValues |> List.map mapValue) + + List a itemValues -> + Scala.Apply + (Scala.Ref [ "morphir", "sdk" ] "List") + (itemValues + |> List.map mapValue + |> List.map (Scala.ArgValue Nothing) + ) + + Record a fieldValues -> + Scala.StructuralValue + (fieldValues + |> List.map + (\( fieldName, fieldValue ) -> + ( fieldName |> Name.toCamelCase, mapValue fieldValue ) + ) + ) - --| Constructor a FQName - --| Tuple a (List (Value a)) - --| List a (List (Value a)) - --| Record a (List ( Name, Value a )) Variable a name -> - Scala.Var (name |> Name.toCamelCase) + Scala.Variable (name |> Name.toCamelCase) Reference a fQName -> let @@ -362,21 +415,133 @@ mapValue value = in Scala.Ref path (name |> Name.toCamelCase) - --| Field a (Value a) Name - --| FieldFunction a Name + Field a subjectValue fieldName -> + Scala.Select (mapValue subjectValue) (fieldName |> Name.toCamelCase) + + FieldFunction a fieldName -> + Scala.Select Scala.Wildcard (fieldName |> Name.toCamelCase) + Apply a fun arg -> - Scala.Apply (mapValue fun) [ Scala.ArgValue Nothing (mapValue arg) ] - - --| Lambda a (Pattern a) (Value a) - --| LetDefinition a Name (Definition a) (Value a) - --| LetRecursion a (Dict Name (Definition a)) (Value a) - --| Destructure a (Pattern a) (Value a) (Value a) - --| IfThenElse a (Value a) (Value a) (Value a) - --| PatternMatch a (Value a) (List ( Pattern a, Value a )) - --| UpdateRecord a (Value a) (List ( Name, Value a )) - --| Unit a - _ -> - Scala.Tuple [] + let + ( bottomFun, args ) = + Value.uncurryApply fun arg + in + Scala.Apply (mapValue bottomFun) + (args + |> List.map + (\argValue -> + Scala.ArgValue Nothing (mapValue argValue) + ) + ) + + Lambda a argPattern bodyValue -> + case argPattern of + AsPattern _ (WildcardPattern _) alias -> + Scala.Lambda [ alias |> Name.toCamelCase ] (mapValue bodyValue) + + _ -> + Scala.MatchCases [ ( mapPattern argPattern, mapValue bodyValue ) ] + + LetDefinition a defName def inValue -> + Scala.Block + [ Scala.FunctionDecl + { modifiers = [] + , name = defName |> Name.toCamelCase + , typeArgs = [] + , args = + if List.isEmpty def.inputTypes then + [] + + else + [ def.inputTypes + |> List.map + (\( argName, _, argType ) -> + { modifiers = [] + , tpe = mapType argType + , name = argName |> Name.toCamelCase + , defaultValue = Nothing + } + ) + ] + , returnType = + Just (mapType def.outputType) + , body = + Just (mapValue def.body) + } + ] + (mapValue inValue) + + LetRecursion a defs inValue -> + Scala.Block + (defs + |> Dict.toList + |> List.map + (\( defName, def ) -> + Scala.FunctionDecl + { modifiers = [] + , name = defName |> Name.toCamelCase + , typeArgs = [] + , args = + if List.isEmpty def.inputTypes then + [] + + else + [ def.inputTypes + |> List.map + (\( argName, _, argType ) -> + { modifiers = [] + , tpe = mapType argType + , name = argName |> Name.toCamelCase + , defaultValue = Nothing + } + ) + ] + , returnType = + Just (mapType def.outputType) + , body = + Just (mapValue def.body) + } + ) + ) + (mapValue inValue) + + Destructure a bindPattern bindValue inValue -> + Scala.Block + [ Scala.ValueDecl + { modifiers = [] + , pattern = mapPattern bindPattern + , value = mapValue bindValue + } + ] + (mapValue inValue) + + IfThenElse a condValue thenValue elseValue -> + Scala.IfElse (mapValue condValue) (mapValue thenValue) (mapValue elseValue) + + PatternMatch a onValue cases -> + Scala.Match (mapValue onValue) + (cases + |> List.map + (\( casePattern, caseValue ) -> + ( mapPattern casePattern, mapValue caseValue ) + ) + |> Scala.MatchCases + ) + + UpdateRecord a subjectValue fieldUpdates -> + Scala.Apply + (Scala.Select (mapValue subjectValue) "copy") + (fieldUpdates + |> List.map + (\( fieldName, fieldValue ) -> + Scala.ArgValue + (Just (fieldName |> Name.toCamelCase)) + (mapValue fieldValue) + ) + ) + + Unit a -> + Scala.Unit mapPattern : Pattern a -> Scala.Pattern @@ -414,7 +579,25 @@ mapPattern pattern = (mapPattern tailPattern) LiteralPattern a literal -> - Scala.LiteralMatch (mapLiteral literal) + let + map l = + case l of + BoolLiteral v -> + Scala.BooleanLit v + + CharLiteral v -> + Scala.CharacterLit v + + StringLiteral v -> + Scala.StringLit v + + IntLiteral v -> + Scala.IntegerLit v + + FloatLiteral v -> + Scala.FloatLit v + in + Scala.LiteralMatch (map literal) UnitPattern a -> Scala.WildcardMatch diff --git a/src/Morphir/Scala/PrettyPrinter.elm b/src/Morphir/Scala/PrettyPrinter.elm index 28f03eb40..f9790af0e 100644 --- a/src/Morphir/Scala/PrettyPrinter.elm +++ b/src/Morphir/Scala/PrettyPrinter.elm @@ -1,17 +1,17 @@ {- -Copyright 2020 Morgan Stanley + Copyright 2020 Morgan Stanley -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. -} @@ -117,6 +117,14 @@ mapMemberDecl opt memberDecl = TypeAlias typeAlias -> "type " ++ typeAlias.alias ++ mapTypeArgs opt typeAlias.typeArgs ++ " = " ++ mapType opt typeAlias.tpe + ValueDecl decl -> + concat + [ "val " + , mapPattern decl.pattern + , " = " + , mapValue opt decl.value + ] + FunctionDecl decl -> let modifierDoc = @@ -142,6 +150,9 @@ mapMemberDecl opt memberDecl = bodyDoc = case decl.body of + Just ((Block _ _) as value) -> + " = " ++ mapValue opt value + Just value -> " =" ++ newLine ++ indent opt.indentDepth (mapValue opt value) @@ -285,13 +296,18 @@ mapType opt tpe = ) StructuralType memberDecls -> - "{ " - ++ (memberDecls + if List.isEmpty memberDecls then + "{}" + + else + concat + [ "{ " + , memberDecls |> List.map (mapMemberDecl opt) |> List.intersperse "; " |> concat - ) - ++ " }" + , " }" + ] FunctionType argType returnType -> (case argType of @@ -314,7 +330,7 @@ mapValue opt value = Literal lit -> mapLit lit - Var name -> + Variable name -> name Ref path name -> @@ -327,7 +343,7 @@ mapValue opt value = "_" Apply funValue argValues -> - parens (mapValue opt funValue) ++ argValueBlock opt argValues + mapValue opt funValue ++ argValueBlock opt argValues UnOp op right -> op ++ mapValue opt right @@ -350,21 +366,14 @@ mapValue opt value = ++ newLine ++ indent opt.indentDepth (mapValue opt bodyValue) - LetBlock bindings inValue -> + Block decls returnValue -> let - bindingStatements = - bindings - |> List.map - (\( bindingPattern, bindingValue ) -> - "val " - ++ mapPattern bindingPattern - ++ " =" - ++ newLine - ++ indent opt.indentDepth (mapValue opt bindingValue) - ) + declDocs = + decls + |> List.map (mapMemberDecl opt) statements = - bindingStatements ++ [ inValue |> mapValue opt ] + declDocs ++ [ returnValue |> mapValue opt ] in statements |> List.intersperse empty @@ -382,18 +391,19 @@ mapValue opt value = mapValue opt targetValue ++ " match " ++ mapValue opt casesValue IfElse condValue trueValue falseValue -> - "if " - ++ parens (mapValue opt condValue) - ++ " " - ++ statementBlock opt [ trueValue |> mapValue opt ] - ++ " else " - ++ (case falseValue of - IfElse _ _ _ -> - mapValue opt falseValue - - _ -> - statementBlock opt [ mapValue opt falseValue ] - ) + concat + [ "if " + , parens (mapValue opt condValue) + , " " + , statementBlock opt [ trueValue |> mapValue opt ] + , " else " + , case falseValue of + IfElse _ _ _ -> + mapValue opt falseValue + + _ -> + statementBlock opt [ mapValue opt falseValue ] + ] Tuple elemValues -> parens @@ -403,6 +413,27 @@ mapValue opt value = |> concat ) + StructuralValue fieldValues -> + if List.isEmpty fieldValues then + "new {}" + + else + concat + [ "new {" + , newLine + , fieldValues + |> List.map + (\( fieldName, fieldValue ) -> + concat [ "def ", fieldName, " = ", mapValue opt fieldValue ] + ) + |> indentLines opt.indentDepth + , newLine + , "}" + ] + + Unit -> + "{}" + CommentedValue childValue message -> mapValue opt childValue ++ " /* " ++ message ++ " */ " @@ -486,6 +517,7 @@ statementBlock opt statements = [ "{" , newLine , indentLines opt.indentDepth statements + , newLine , "}" ] diff --git a/tests-integration/reference-model/elm.json b/tests-integration/reference-model/elm.json new file mode 100644 index 000000000..dea3450db --- /dev/null +++ b/tests-integration/reference-model/elm.json @@ -0,0 +1,24 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/tests-integration/reference-model/morphir.json b/tests-integration/reference-model/morphir.json new file mode 100644 index 000000000..8647ef41c --- /dev/null +++ b/tests-integration/reference-model/morphir.json @@ -0,0 +1,8 @@ +{ + "name": "Morphir.Reference.Model", + "sourceDirectory": "src", + "exposedModules": [ + "Types", + "Values" + ] +} \ No newline at end of file diff --git a/tests-integration/reference-model/src/Morphir/Reference/Model/Types.elm b/tests-integration/reference-model/src/Morphir/Reference/Model/Types.elm new file mode 100644 index 000000000..e432aca88 --- /dev/null +++ b/tests-integration/reference-model/src/Morphir/Reference/Model/Types.elm @@ -0,0 +1,23 @@ +module Morphir.Reference.Model.Types exposing (..) + +{-| Various examples of types for testing. +-} + + +{-| Alias referring to another type using a reference. +-} +type alias Quantity = + Int + + +type Custom + = CustomNoArg + | CustomOneArg Bool + | CustomTwoArg String Quantity + + +type alias FooBarBazRecord = + { foo : String + , bar : Bool + , baz : Int + } diff --git a/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm b/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm new file mode 100644 index 000000000..6accfaca2 --- /dev/null +++ b/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm @@ -0,0 +1,170 @@ +module Morphir.Reference.Model.Values exposing (..) + +import Morphir.Reference.Model.Types exposing (Custom(..), FooBarBazRecord) + + +basicLiteralBool : Bool +basicLiteralBool = + True + + +basicLiteralChar : Char +basicLiteralChar = + 'Z' + + +basicLiteralString : String +basicLiteralString = + "foo bar" + + +basicLiteralInt : Int +basicLiteralInt = + 42 + + +basicLiteralFloat : Float +basicLiteralFloat = + 3.14 + + +basicConstructor1 : Custom +basicConstructor1 = + CustomNoArg + + +basicConstructor2 : Custom +basicConstructor2 = + CustomOneArg False + + +basicConstructor3 : Custom +basicConstructor3 = + CustomTwoArg "Baz" 12345 + + +basicTuple2 : ( Int, String ) +basicTuple2 = + ( 13, "Tuple Two" ) + + +basicTuple3 : ( Bool, Int, Bool ) +basicTuple3 = + ( True, 14, False ) + + +basicListEmpty : List Int +basicListEmpty = + [] + + +basicListOne : List String +basicListOne = + [ "single element" ] + + +basicListMany : List Char +basicListMany = + [ 'a', 'b', 'c', 'd' ] + + +basicRecordEmpty : {} +basicRecordEmpty = + {} + + +basicRecordOne : { foo : String } +basicRecordOne = + { foo = "bar" + } + + +basicRecordMany : { foo : String, bar : Bool, baz : Int } +basicRecordMany = + { foo = "bar" + , bar = False + , baz = 15 + } + + +basicField : { foo : String } -> String +basicField rec = + rec.foo + + +basicFieldFunction : { foo : String } -> String +basicFieldFunction = + .foo + + +basicLetDefinition : Int +basicLetDefinition = + let + a : Int + a = + 1 + + b : Int + b = + a + + d : Int -> Int + d i = + i + in + d b + + +basicLetRecursion : Int +basicLetRecursion = + let + a : Int -> Int + a i = + b (i - 1) + + b : Int -> Int + b i = + if i < 0 then + 0 + + else + a i + in + a 10 + + +basicDestructure : int +basicDestructure = + let + ( a, b ) = + ( 1, 2 ) + in + b + + +basicIfThenElse : Int -> Int -> String +basicIfThenElse a b = + if a < b then + "Less" + + else + "Greater or equal" + + +basicPatternMatchWildcard : String -> Int +basicPatternMatchWildcard s = + case s of + _ -> + 1 + + +basicUpdateRecord : FooBarBazRecord -> FooBarBazRecord +basicUpdateRecord rec = + { rec + | baz = rec.baz + 1 + } + + +basicUnit : () +basicUnit = + () From 15e664cf1165b88dcb27669aa221649cbf52e79f Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Fri, 14 Aug 2020 17:54:40 -0400 Subject: [PATCH 9/9] Fixed typo. #109 --- .../reference-model/src/Morphir/Reference/Model/Values.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm b/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm index 6accfaca2..5642919d3 100644 --- a/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm +++ b/tests-integration/reference-model/src/Morphir/Reference/Model/Values.elm @@ -133,7 +133,7 @@ basicLetRecursion = a 10 -basicDestructure : int +basicDestructure : Int basicDestructure = let ( a, b ) =