From 965db7217e819351c2d0e321a9ca2529f13163b3 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Tue, 25 Aug 2020 11:12:29 -0400 Subject: [PATCH 1/6] Fix elm-syntax version in CLI. --- cli/elm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/elm.json b/cli/elm.json index 074d6ae86..9205ce980 100644 --- a/cli/elm.json +++ b/cli/elm.json @@ -17,7 +17,7 @@ "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" + "stil4m/elm-syntax": "7.1.3" }, "indirect": { "avh4/elm-fifo": "1.0.4", From b916218da3c8b64bd789f78ee6eb05d1f3688009 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Tue, 25 Aug 2020 14:59:46 -0400 Subject: [PATCH 2/6] Fix elm-syntax operator precedence. --- src/Morphir/Elm/Frontend.elm | 41 +++++--- src/Morphir/Elm/WellKnownOperators.elm | 125 +++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 src/Morphir/Elm/WellKnownOperators.elm diff --git a/src/Morphir/Elm/Frontend.elm b/src/Morphir/Elm/Frontend.elm index 1ab8f050c..d7706dea4 100644 --- a/src/Morphir/Elm/Frontend.elm +++ b/src/Morphir/Elm/Frontend.elm @@ -39,7 +39,7 @@ module Morphir.Elm.Frontend exposing import Dict exposing (Dict) import Elm.Parser -import Elm.Processing as Processing +import Elm.Processing as Processing exposing (ProcessContext) import Elm.RawFile as RawFile exposing (RawFile) import Elm.Syntax.Declaration exposing (Declaration(..)) import Elm.Syntax.Exposing as Exposing exposing (Exposing) @@ -54,6 +54,7 @@ import Elm.Syntax.Range as Range exposing (Range) import Elm.Syntax.TypeAnnotation exposing (TypeAnnotation(..)) import Graph exposing (Graph) import Morphir.Elm.Frontend.Resolve as Resolve exposing (ModuleResolver) +import Morphir.Elm.WellKnownOperators as WellKnownOperators import Morphir.Graph import Morphir.IR.AccessControlled exposing (AccessControlled, private, public) import Morphir.IR.Documented exposing (Documented) @@ -275,27 +276,40 @@ packageDefinitionFromSource packageInfo dependencies sourceFiles = mapParsedFiles : Dict Path (Package.Specification ()) -> Path -> Dict ModuleName ParsedFile -> List ModuleName -> Result Errors (Dict Path (Module.Definition SourceLocation)) mapParsedFiles dependencies currentPackagePath parsedModules sortedModuleNames = + let + initialContext : ProcessContext + initialContext = + Processing.init + |> withWellKnownOperators + in sortedModuleNames |> List.filterMap (\moduleName -> parsedModules |> Dict.get moduleName ) - |> List.map - (\parsedFile -> - parsedFile.rawFile - |> Processing.process Processing.init - |> ProcessedFile parsedFile - ) |> List.foldl - (\processedFile moduleResultsSoFar -> + (\parsedFile moduleResultsSoFar -> moduleResultsSoFar |> Result.andThen - (\modulesSoFar -> - mapProcessedFile dependencies currentPackagePath processedFile modulesSoFar + (\( processContext, modulesSoFar ) -> + let + processedFile : File + processedFile = + parsedFile.rawFile + |> Processing.process processContext + + newProcessContext : ProcessContext + newProcessContext = + processContext + |> Processing.addFile parsedFile.rawFile + in + mapProcessedFile dependencies currentPackagePath (ProcessedFile parsedFile processedFile) modulesSoFar + |> Result.map (Tuple.pair newProcessContext) ) ) - (Ok Dict.empty) + (Ok ( initialContext, Dict.empty )) + |> Result.map Tuple.second mapProcessedFile : Dict Path (Package.Specification ()) -> Path -> ProcessedFile -> Dict Path (Module.Definition SourceLocation) -> Result Errors (Dict Path (Module.Definition SourceLocation)) @@ -1623,3 +1637,8 @@ fixAssociativity expr = _ -> expr + + +withWellKnownOperators : ProcessContext -> ProcessContext +withWellKnownOperators context = + List.foldl Processing.addDependency context WellKnownOperators.wellKnownOperators diff --git a/src/Morphir/Elm/WellKnownOperators.elm b/src/Morphir/Elm/WellKnownOperators.elm new file mode 100644 index 000000000..9e8b18b24 --- /dev/null +++ b/src/Morphir/Elm/WellKnownOperators.elm @@ -0,0 +1,125 @@ +module Morphir.Elm.WellKnownOperators exposing (wellKnownOperators) + +-- Temporarily included until this is released in elm-syntax + +import Dict +import Elm.Dependency exposing (Dependency) +import Elm.Interface exposing (Exposed(..)) +import Elm.Syntax.Infix exposing (InfixDirection(..)) +import Elm.Syntax.Node exposing (Node(..)) +import Elm.Syntax.Range exposing (emptyRange) + + +wellKnownOperators : List Dependency +wellKnownOperators = + [ elmCore ] + + +elmCore : Dependency +elmCore = + { interfaces = + Dict.fromList + [ ( [ "Array" ] + , [ CustomType ( "Array", [] ) + , Function "empty" + , Function "isEmpty" + , Function "length" + , Function "initialize" + , Function "repeat" + , Function "fromList" + , Function "get" + , Function "set" + , Function "push" + , Function "toList" + , Function "toIndexedList" + , Function "foldr" + , Function "foldl" + , Function "filter" + , Function "map" + , Function "indexedMap" + , Function "append" + , Function "slice" + ] + ) + , ( [ "Basics" ] + , [ CustomType ( "Int", [] ) + , CustomType ( "Float", [] ) + , Operator { direction = Node emptyRange Left, function = Node emptyRange "add", operator = Node emptyRange "+", precedence = Node emptyRange 6 } + , Operator { direction = Node emptyRange Left, function = Node emptyRange "sub", operator = Node emptyRange "-", precedence = Node emptyRange 6 } + , Operator { direction = Node emptyRange Left, function = Node emptyRange "mul", operator = Node emptyRange "*", precedence = Node emptyRange 7 } + , Operator { direction = Node emptyRange Left, function = Node emptyRange "fdiv", operator = Node emptyRange "/", precedence = Node emptyRange 7 } + , Operator { direction = Node emptyRange Left, function = Node emptyRange "idiv", operator = Node emptyRange "//", precedence = Node emptyRange 7 } + , Operator { direction = Node emptyRange Right, function = Node emptyRange "pow", operator = Node emptyRange "^", precedence = Node emptyRange 8 } + , Function "toFloat" + , Function "round" + , Function "floor" + , Function "ceiling" + , Function "truncate" + , Operator { direction = Node emptyRange Non, function = Node emptyRange "eq", operator = Node emptyRange "==", precedence = Node emptyRange 4 } + , Operator { direction = Node emptyRange Non, function = Node emptyRange "neq", operator = Node emptyRange "/=", precedence = Node emptyRange 4 } + , Operator { direction = Node emptyRange Non, function = Node emptyRange "lt", operator = Node emptyRange "<", precedence = Node emptyRange 4 } + , Operator { direction = Node emptyRange Non, function = Node emptyRange "gt", operator = Node emptyRange ">", precedence = Node emptyRange 4 } + , Operator { direction = Node emptyRange Non, function = Node emptyRange "le", operator = Node emptyRange "<=", precedence = Node emptyRange 4 } + , Operator { direction = Node emptyRange Non, function = Node emptyRange "ge", operator = Node emptyRange ">=", precedence = Node emptyRange 4 } + , Function "max" + , Function "min" + , Function "compare" + , CustomType ( "Order", [ "LT", "EQ", "GT" ] ) + , CustomType ( "Bool", [ "True", "False" ] ) + , Function "not" + , Operator { direction = Node emptyRange Right, function = Node emptyRange "and", operator = Node emptyRange "&&", precedence = Node emptyRange 3 } + , Operator { direction = Node emptyRange Right, function = Node emptyRange "or", operator = Node emptyRange "||", precedence = Node emptyRange 2 } + , Function "xor" + , Operator { direction = Node emptyRange Right, function = Node emptyRange "append", operator = Node emptyRange "++", precedence = Node emptyRange 5 } + , Function "modBy" + , Function "remainderBy" + , Function "negate" + , Function "abs" + , Function "clamp" + , Function "sqrt" + , Function "logBase" + , Function "e" + , Function "pi" + , Function "cos" + , Function "sin" + , Function "tan" + , Function "acos" + , Function "asin" + , Function "atan" + , Function "atan2" + , Function "degrees" + , Function "radians" + , Function "turns" + , Function "toPolar" + , Function "fromPolar" + , Function "isNaN" + , Function "isInfinite" + , Function "identity" + , Function "always" + , Operator { direction = Node emptyRange Right, function = Node emptyRange "apL", operator = Node emptyRange "<|", precedence = Node emptyRange 0 } + , Operator { direction = Node emptyRange Left, function = Node emptyRange "apR", operator = Node emptyRange "|>", precedence = Node emptyRange 0 } + , Operator { direction = Node emptyRange Left, function = Node emptyRange "composeL", operator = Node emptyRange "<<", precedence = Node emptyRange 9 } + , Operator { direction = Node emptyRange Right, function = Node emptyRange "composeR", operator = Node emptyRange ">>", precedence = Node emptyRange 9 } + , CustomType ( "Never", [] ) + , Function "never" + ] + ) + , ( [ "Bitwise" ], [ Function "and", Function "or", Function "xor", Function "complement", Function "shiftLeftBy", Function "shiftRightBy", Function "shiftRightZfBy" ] ) + , ( [ "Char" ], [ CustomType ( "Char", [] ), Function "isUpper", Function "isLower", Function "isAlpha", Function "isAlphaNum", Function "isDigit", Function "isOctDigit", Function "isHexDigit", Function "toUpper", Function "toLower", Function "toLocaleUpper", Function "toLocaleLower", Function "toCode", Function "fromCode" ] ) + , ( [ "Debug" ], [ Function "toString", Function "log", Function "todo" ] ) + , ( [ "Dict" ], [ CustomType ( "Dict", [] ), Function "empty", Function "singleton", Function "insert", Function "update", Function "remove", Function "isEmpty", Function "member", Function "get", Function "size", Function "keys", Function "values", Function "toList", Function "fromList", Function "map", Function "foldl", Function "foldr", Function "filter", Function "partition", Function "union", Function "intersect", Function "diff", Function "merge" ] ) + , ( [ "List" ], [ Function "singleton", Function "repeat", Function "range", Operator { direction = Node emptyRange Right, function = Node emptyRange "cons", operator = Node emptyRange "::", precedence = Node emptyRange 5 }, Function "map", Function "indexedMap", Function "foldl", Function "foldr", Function "filter", Function "filterMap", Function "length", Function "reverse", Function "member", Function "all", Function "any", Function "maximum", Function "minimum", Function "sum", Function "product", Function "append", Function "concat", Function "concatMap", Function "intersperse", Function "map2", Function "map3", Function "map4", Function "map5", Function "sort", Function "sortBy", Function "sortWith", Function "isEmpty", Function "head", Function "tail", Function "take", Function "drop", Function "partition", Function "unzip" ] ) + , ( [ "Maybe" ], [ CustomType ( "Maybe", [ "Just", "Nothing" ] ), Function "andThen", Function "map", Function "map2", Function "map3", Function "map4", Function "map5", Function "withDefault" ] ) + , ( [ "Platform" ], [ CustomType ( "Program", [] ), Function "worker", CustomType ( "Task", [] ), CustomType ( "ProcessId", [] ), CustomType ( "Router", [] ), Function "sendToApp", Function "sendToSelf" ] ) + , ( [ "Platform", "Cmd" ], [ CustomType ( "Cmd", [] ), Function "none", Function "batch", Function "map" ] ) + , ( [ "Platform", "Sub" ], [ CustomType ( "Sub", [] ), Function "none", Function "batch", Function "map" ] ) + , ( [ "Process" ], [ Alias "Id", Function "spawn", Function "sleep", Function "kill" ] ) + , ( [ "Result" ], [ CustomType ( "Result", [ "Ok", "Err" ] ), Function "withDefault", Function "map", Function "map2", Function "map3", Function "map4", Function "map5", Function "andThen", Function "toMaybe", Function "fromMaybe", Function "mapError" ] ) + , ( [ "Set" ], [ CustomType ( "Set", [] ), Function "empty", Function "singleton", Function "insert", Function "remove", Function "isEmpty", Function "member", Function "size", Function "union", Function "intersect", Function "diff", Function "toList", Function "fromList", Function "map", Function "foldl", Function "foldr", Function "filter", Function "partition" ] ) + , ( [ "String" ], [ CustomType ( "String", [] ), Function "isEmpty", Function "length", Function "reverse", Function "repeat", Function "replace", Function "append", Function "concat", Function "split", Function "join", Function "words", Function "lines", Function "slice", Function "left", Function "right", Function "dropLeft", Function "dropRight", Function "contains", Function "startsWith", Function "endsWith", Function "indexes", Function "indices", Function "toInt", Function "fromInt", Function "toFloat", Function "fromFloat", Function "fromChar", Function "cons", Function "uncons", Function "toList", Function "fromList", Function "toUpper", Function "toLower", Function "pad", Function "padLeft", Function "padRight", Function "trim", Function "trimLeft", Function "trimRight", Function "map", Function "filter", Function "foldl", Function "foldr", Function "any", Function "all" ] ) + , ( [ "Task" ], [ Alias "Task", Function "succeed", Function "fail", Function "map", Function "map2", Function "map3", Function "map4", Function "map5", Function "sequence", Function "andThen", Function "onError", Function "mapError", Function "perform", Function "attempt" ] ) + , ( [ "Tuple" ], [ Function "pair", Function "first", Function "second", Function "mapFirst", Function "mapSecond", Function "mapBoth" ] ) + ] + , name = "elm/core" + , version = "1.0.5" + } From 16e46bd9ce74b36a5d6ec16296697cd62aab0fca Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Tue, 25 Aug 2020 15:00:25 -0400 Subject: [PATCH 3/6] Use better literal wrappers in generated Scala. --- src/Morphir/Scala/Backend.elm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index 792061144..d450600ac 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -354,27 +354,27 @@ mapValue value = case value of Literal a literal -> let - wrap : String -> Scala.Lit -> Scala.Value - wrap moduleName lit = + wrap : List String -> String -> Scala.Lit -> Scala.Value + wrap modulePath moduleName lit = Scala.Apply - (Scala.Ref [ "morphir", "sdk" ] moduleName) + (Scala.Ref modulePath moduleName) [ Scala.ArgValue Nothing (Scala.Literal lit) ] in case literal of BoolLiteral v -> - wrap "Bool" (Scala.BooleanLit v) + wrap [ "morphir", "sdk", "Basics" ] "Bool" (Scala.BooleanLit v) CharLiteral v -> - wrap "Char" (Scala.CharacterLit v) + wrap [ "morphir", "sdk", "Char" ] "Char" (Scala.CharacterLit v) StringLiteral v -> - wrap "String" (Scala.StringLit v) + wrap [ "morphir", "sdk", "String" ] "String" (Scala.StringLit v) IntLiteral v -> - wrap "Int" (Scala.IntegerLit v) + wrap [ "morphir", "sdk", "Basics" ] "Int" (Scala.IntegerLit v) FloatLiteral v -> - wrap "Float" (Scala.FloatLit v) + wrap [ "morphir", "sdk", "Basics" ] "Float" (Scala.FloatLit v) Constructor a fQName -> let From 6135d786a27d6dbf29331caf45c32160c32b85f3 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Fri, 28 Aug 2020 14:50:31 -0400 Subject: [PATCH 4/6] Generate case objects for no-argument type constructors. #105 --- src/Morphir/Scala/Backend.elm | 43 +++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index d450600ac..d400b4882 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -233,23 +233,32 @@ mapCustomTypeDefinition : Package.PackagePath -> Path -> Name -> List Name -> Ac 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 - } + if List.isEmpty args then + Scala.Object + { modifiers = [ Scala.Case ] + , name = name |> Name.toTitleCase + , extends = extends + , members = [] + } + + else + 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) From bb3b936ffbb377f86c0a029b080a42ff3b7f51cb Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Tue, 1 Sep 2020 13:54:17 -0400 Subject: [PATCH 5/6] Flatten nested let expressions. #140 --- src/Morphir/Scala/Backend.elm | 73 ++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index d400b4882..99b14de40 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -451,34 +451,55 @@ mapValue value = _ -> Scala.MatchCases [ ( mapPattern argPattern, mapValue bodyValue ) ] - LetDefinition a defName def inValue -> + LetDefinition _ _ _ _ -> + let + flattenLetDef : Value a -> ( List ( Name, Value.Definition a ), Value a ) + flattenLetDef v = + case v of + LetDefinition a dName d inV -> + let + ( nestedDefs, nestedInValue ) = + flattenLetDef inV + in + ( ( dName, d ) :: nestedDefs, nestedInValue ) + + _ -> + ( [], v ) + + ( defs, finalInValue ) = + flattenLetDef value + in Scala.Block - [ Scala.FunctionDecl - { modifiers = [] - , name = defName |> Name.toCamelCase - , typeArgs = [] - , args = - if List.isEmpty def.inputTypes then - [] + (defs + |> 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) + 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 finalInValue) LetRecursion a defs inValue -> Scala.Block From 04ee13fc732e58784d7bfdc28648f6cf77bcd15f Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Tue, 1 Sep 2020 14:05:15 -0400 Subject: [PATCH 6/6] Use val instead of def when there are no arguments. #141 --- src/Morphir/Scala/Backend.elm | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Morphir/Scala/Backend.elm b/src/Morphir/Scala/Backend.elm index 99b14de40..01b611982 100644 --- a/src/Morphir/Scala/Backend.elm +++ b/src/Morphir/Scala/Backend.elm @@ -473,15 +473,19 @@ mapValue value = (defs |> List.map (\( defName, def ) -> - Scala.FunctionDecl - { modifiers = [] - , name = defName |> Name.toCamelCase - , typeArgs = [] - , args = - if List.isEmpty def.inputTypes then - [] + if List.isEmpty def.inputTypes then + Scala.ValueDecl + { modifiers = [] + , pattern = Scala.NamedMatch (defName |> Name.toCamelCase) + , value = mapValue def.body + } - else + else + Scala.FunctionDecl + { modifiers = [] + , name = defName |> Name.toCamelCase + , typeArgs = [] + , args = [ def.inputTypes |> List.map (\( argName, _, argType ) -> @@ -492,11 +496,11 @@ mapValue value = } ) ] - , returnType = - Just (mapType def.outputType) - , body = - Just (mapValue def.body) - } + , returnType = + Just (mapType def.outputType) + , body = + Just (mapValue def.body) + } ) ) (mapValue finalInValue)