@@ -2,155 +2,169 @@
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Canonicalize.Effects
3module Canonicalize.Effects
4 ( canonicalize
4 ( canonicalize
5− , toExposedValues
5+ , checkPayload
10import qualified Data.Foldable as F
9import qualified Data.Foldable as F
10+import qualified Data.Map as Map
11import qualified Data.Set as Set
11import qualified Data.Set as Set
12import Data.Text (Text)
12import Data.Text (Text)
14−import qualified AST.Effects as Effects
14+import qualified AST.Expression.Canonical as Can
15+import qualified AST.Expression.Valid as Valid
16+import qualified AST.Module.Name as ModuleName
15−import qualified AST.Type as T
17+import qualified AST.Type as Type
16−import qualified AST.Variable as Var
18+import qualified Canonicalize.Environment as Env
19+import qualified Canonicalize.Type as Type
20+import qualified Elm.Name as N
17import qualified Reporting.Annotation as A
21import qualified Reporting.Annotation as A
18import qualified Reporting.Error.Canonicalize as Error
22import qualified Reporting.Error.Canonicalize as Error
19−import qualified Reporting.Region as R
20import qualified Reporting.Result as Result
23import qualified Reporting.Result as Result
21−import qualified Canonicalize.Environment as Env
24+import qualified Reporting.Warning as Warning
22−import qualified Canonicalize.Type as Canonicalize
25+
23−import Canonicalize.Variable (Result)
26+
32+ Result.Result () Warning.Warning Error.Error a
27−-- TO EXPORT VALUES
35+
30−toExposedValues :: Effects.Raw -> [Text]
39+canonicalize :: Env.Env -> Map.Map N.Name a -> Valid.Effects -> Result Can.Effects
31−toExposedValues effects =
40+canonicalize env unions effects =
32 case effects of
41 case effects of
33− Effects.None ->
42+ Valid.NoEffects ->
34− []
43+ Result.ok Can.NoEffects
36− Effects.Manager _ _ ->
45+ Valid.Ports ports ->
37− []
46+ do pairs <- traverse (canonicalizePort env) ports
47+ return $ Can.Ports (Map.fromList pairs)
39− Effects.Port ports ->
49+ Valid.Cmd cmdType ->
40− map (Effects._rawName . A.drop) ports
50+ Can.Cmd <$> verifyEffectType cmdType unions
53+ Can.Sub <$> verifyEffectType subType unions
55+ Valid.Fx cmdType subType ->
57+ <$> verifyEffectType cmdType unions
58+ <*> verifyEffectType subType unions
61+verifyEffectType :: A.Located N.Name -> Map.Map N.Name a -> Result N.Name
62+verifyEffectType (A.A region name) unions =
63+ if Map.member name unions then
66+ Result.throw region (Error.EffectNotFound name)
47−canonicalize :: Env.Env -> Effects.Raw -> Result Effects.Canonical
48−canonicalize env effects =
51− Result.ok Effects.None
53− Effects.Manager _ info ->
69+canonicalizePort :: Env.Env -> Valid.Port -> Result (N.Name, Can.Port)
54− Result.ok (Effects.Manager (Env.getPackage env) info)
70+canonicalizePort env (Valid.Port (A.A region portName) tipe) =
71+ do ctipe <- Type.canonicalize env tipe
72+ case Type.deepDealias ctipe of
73+ Type.Lambda outgoingType (Type.Type home name [Type.Var _])
74+ | home == ModuleName.cmd && name == "Cmd" ->
75+ case checkPayload outgoingType of
76+ Left (badType, err) ->
77+ Result.throw region (Error.PortPayloadInvalid portName badType err)
80+ Result.ok (portName, Can.Outgoing ctipe)
82+ Type.Lambda (Type.Lambda incomingType (Type.Var msg1)) (Type.Type home name [Type.Var msg2])
83+ | home == ModuleName.sub && name == "Sub" && msg1 == msg2 ->
84+ case checkPayload incomingType of
85+ Left (badType, err) ->
86+ Result.throw region (Error.PortPayloadInvalid portName badType err)
56− Effects.Port rawPorts ->
88+ Right () ->
57− Effects.Port <$> traverse (canonicalizeRawPort env) rawPorts
89+ Result.ok (portName, Can.Incoming ctipe)
92+ Result.throw region (Error.PortTypeInvalid portName ctipe)
62− -> A.Commented Effects.PortRaw
63− -> Result (A.Commented Effects.PortCanonical)
64−canonicalizeRawPort env (A.A ann (Effects.PortRaw name rawType)) =
65− do tipe <- Canonicalize.tipe env rawType
66− kind <- figureOutKind (fst ann) name tipe
67− Result.ok (A.A ann (Effects.PortCanonical name kind tipe))
70−figureOutKind :: R.Region -> Text -> T.Canonical -> Result Effects.Kind
96+-- CHECK PAYLOAD TYPES
71−figureOutKind region name rootType =
72− case T.deepDealias rootType of
73− T.Lambda outgoingType (T.Type effect [T.Var _])
74− | effect == Var.cmd ->
75− pure (Effects.Outgoing outgoingType)
76− <* checkPortType (makeError region name) outgoingType
78− T.Lambda (T.Lambda incomingType (T.Var msg1)) (T.Type effect [T.Var msg2])
79− | effect == Var.sub && msg1 == msg2 ->
80− pure (Effects.Incoming incomingType)
81− <* checkPortType (makeError region name) incomingType
83− _ ->
99+checkPayload :: Type.Canonical -> Either (Type.Canonical, Error.InvalidPayload) ()
84− Result.throw region (Error.BadPort name rootType)
100+checkPayload tipe =
102+ Type.Aliased _ _ args aliasedType ->
103+ checkPayload (Type.dealias args aliasedType)
105+ Type.Type home name args ->
108+ | isPrim home name -> Right ()
109+ | isJson home name -> Right ()
87−makeError :: R.Region -> Text -> T.Canonical -> Maybe Text -> A.Located Error.Error
111+ [arg]
88−makeError region name tipe maybeMessage =
112+ | isList home name -> checkPayload arg
89− A.A region (Error.port name tipe maybeMessage)
113+ | isMaybe home name -> checkPayload arg
114+ | isArray home name -> checkPayload arg
117+ Left (tipe, Error.UnsupportedType name)
93−-- CHECK INCOMING AND OUTGOING TYPES
122+ Type.Tuple a b cs ->
123+ F.traverse_ checkPayload (a:b:cs)
126+ Left (tipe, Error.TypeVariable name)
96−checkPortType
128+ Type.Lambda _ _ ->
97− :: (Monoid i)
129+ Left (tipe, Error.Function)
98− => (T.Canonical -> Maybe Text -> A.Located e)
100− -> Result.Result i w e ()
101−checkPortType mkError tipe =
104− checkPortType mkError
106− throw maybeMsg =
131+ Type.Record _ (Just _) ->
107− Result.throwMany [mkError tipe maybeMsg]
132+ Left (tipe, Error.ExtendedRecord)
110− T.Aliased _ args aliasedType ->
111− check (T.dealias args aliasedType)
113− T.Type name [] ->
134+ Type.Record fields Nothing ->
114− if Var.isJson name || isPrimitive name || Var.isTuple name then
135+ F.traverse_ checkPayload fields
120− T.Type name [arg]
138+isPrim :: ModuleName.Canonical -> N.Name -> Bool
121− | Var.isMaybe name -> check arg
139+isPrim home name =
122− | Var.isArray name -> check arg
140+ home == ModuleName.basics
123− | Var.isList name -> check arg
141+ && Set.member name primitives
126− if Var.isTuple name then
127− F.traverse_ check args
131− T.Var _ ->
144+primitives :: Set.Set Text
132− throw (Just "free type variable")
145+primitives =
146+ Set.fromList [ "Int", "Float", "String", "Bool" ]
135− throw (Just "function")
137− T.Record _ (Just _) ->
149+isJson :: ModuleName.Canonical -> N.Name -> Bool
138− throw (Just "extended record")
150+isJson home name =
151+ home == ModuleName.jsonEncode
140− T.Record fields Nothing ->
141− F.traverse_ (\(k,v) -> (,) k <$> check v) fields
155+isList :: ModuleName.Canonical -> N.Name -> Bool
157+ home == ModuleName.list
144−isPrimitive :: Var.Canonical -> Bool
147− Var.Canonical Var.BuiltIn name ->
148− Set.member name primitiveSet
150− _ ->
161+isMaybe :: ModuleName.Canonical -> N.Name -> Bool
151− False
162+isMaybe home name =
163+ home == ModuleName.maybe
154−primitiveSet :: Set.Set Text
167+isArray :: ModuleName.Canonical -> N.Name -> Bool
155−primitiveSet =
168+isArray home name =
156− Set.fromList ["Int","Float","String","Bool"]
169+ home == ModuleName.array