on master help sign in
Merge branch 'pattern-warnings'
commit5baaacfbb5644ec5e608c737518359a91c873756 authorEvan Czaplicki <info@elm-lang.org> dateTue 18 Aug 2015, 19:10 (11 years ago) parented48cd2fe9bd43
25 files changed+1620−369
elm-compiler.cabal+9−2 src/AST/Expression/General.hs+19−3 src/AST/Expression/Optimized.hs+35 src/AST/Literal.hs+10−5 src/AST/Module.hs+10−5 src/AST/Pattern.hs+4 src/Canonicalize.hs+11−6 src/Canonicalize/Sort.hs+17−5 src/Compile.hs+3 src/Elm/Compiler.hs+7−1 src/Generate/JavaScript.hs+602−268 src/Generate/JavaScript/Crash.hs+32 src/Generate/JavaScript/Helpers.hs−19 src/Nitpick/Pattern.hs+71 src/Nitpick/PatternMatches.hs+370 src/Optimize.hs+19 src/Generate/Cases.hs → src/Optimize/Cases.hs+14−10 src/Generate/Substitute.hs → src/Optimize/Substitute.hs+17−14 src/Optimize/TailCalls.hs+239 src/Parse/Expression.hs+27−18 src/Reporting/Crash.hs+20 src/Reporting/Error/Type.hs+7 src/Reporting/Warning.hs+42 src/Type/Constrain/Expression.hs+28−11 src/Validate.hs+7−2
elm-compiler.cabal+9−2
@@ -49,6 +49,7 @@ Library
4949 AST.Declaration,
5050 AST.Expression.Canonical,
5151 AST.Expression.General,
52+ AST.Expression.Optimized,
5253 AST.Expression.Source,
5354 AST.Expression.Valid,
5455 AST.Helpers,
@@ -73,13 +74,18 @@ Library
7374 Elm.Compiler.Imports,
7475 Elm.Compiler.Type.Extract,
7576 Elm.Compiler.Version,
76− Generate.Cases,
7777 Generate.JavaScript,
78+ Generate.JavaScript.Crash,
7879 Generate.JavaScript.Helpers,
7980 Generate.JavaScript.Port,
8081 Generate.JavaScript.Variable,
81− Generate.Substitute,
82+ Nitpick.Pattern,
83+ Nitpick.PatternMatches,
8284 Nitpick.TopLevelTypes,
85+ Optimize,
86+ Optimize.Cases,
87+ Optimize.Substitute,
88+ Optimize.TailCalls,
8389 Parse.Binop,
8490 Parse.Declaration,
8591 Parse.Expression,
@@ -90,6 +96,7 @@ Library
9096 Parse.Pattern,
9197 Parse.Type,
9298 Reporting.Annotation,
99+ Reporting.Crash,
93100 Reporting.Error,
94101 Reporting.Error.Canonicalize,
95102 Reporting.Error.Docs,
src/AST/Expression/General.hs+19−3
@@ -16,6 +16,7 @@ import qualified AST.Pattern as Pattern
1616import qualified AST.Type as Type
1717import qualified AST.Variable as Var
1818import qualified Reporting.Annotation as A
19+import qualified Reporting.Crash as Crash
1920import qualified Reporting.PrettyPrint as P
2021
2122
@@ -48,7 +49,7 @@ data Expr' ann def var typ
4849 | Binop var (Expr ann def var typ) (Expr ann def var typ)
4950 | Lambda (Pattern.Pattern ann var) (Expr ann def var typ)
5051 | App (Expr ann def var typ) (Expr ann def var typ)
51− | MultiIf [(Expr ann def var typ,Expr ann def var typ)]
52+ | MultiIf [(Expr ann def var typ, Expr ann def var typ)] (Expr ann def var typ)
5253 | Let [def] (Expr ann def var typ)
5354 | Case (Expr ann def var typ) [(Pattern.Pattern ann var, Expr ann def var typ)]
5455 | Data String [Expr ann def var typ]
@@ -60,6 +61,7 @@ data Expr' ann def var typ
6061 -- for type checking and code gen only
6162 | Port (PortImpl (Expr ann def var typ) typ)
6263 | GLShader String String Literal.GLShaderTipe
64+ | Crash Crash.Details
6365 deriving (Show)
6466
6567
@@ -161,9 +163,17 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
161163 func:args =
162164 map (P.pretty dealiaser True) (collectApps expr ++ [arg])
163165
164− MultiIf branches ->
166+ MultiIf [(condition, thenBranch)] elseBranch ->
167+ P.parensIf needsParens $ P.sep $
168+ [ P.text "if" <+> P.pretty dealiaser False condition <+> P.text "then"
169+ , P.nest 4 (P.pretty dealiaser False thenBranch)
170+ , P.text "else"
171+ , P.nest 4 (P.pretty dealiaser False elseBranch)
172+ ]
173+
174+ MultiIf branches finally ->
165175 P.parensIf needsParens $
166− P.text "if" $$ nest 3 (vcat $ map iff branches)
176+ P.text "if" $$ nest 3 (vcat $ map iff branches ++ [final])
167177 where
168178 iff (condition, branch) =
169179 P.text "|" <+>
@@ -172,6 +182,9 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
172182 2
173183 (P.pretty dealiaser False branch)
174184
185+ final =
186+ P.hang (P.text "| True -> ") 2 (P.pretty dealiaser False finally)
187+
175188 Let defs body ->
176189 P.parensIf needsParens $
177190 P.sep
@@ -263,6 +276,9 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
263276 Port portImpl ->
264277 P.pretty dealiaser needsParens portImpl
265278
279+ Crash details ->
280+ P.text ("<crash:" ++ Crash.summary details ++ ">")
281+
266282
267283instance P.Pretty (PortImpl expr tipe) where
268284 pretty _ _ impl =
src/AST/Expression/Optimized.hsnew file+35
@@ -0,0 +1,35 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module AST.Expression.Optimized
3+ ( Expr, Expr'
4+ , Def(..)
5+ , Facts(..), dummyFacts
6+ ) where
7+
8+import qualified AST.Expression.General as General
9+import qualified AST.Pattern as Pattern
10+import qualified AST.Type as Type
11+import qualified AST.Variable as Var
12+
13+
14+type Expr =
15+ General.Expr () Def Var.Canonical Type.Canonical
16+
17+
18+type Expr' =
19+ General.Expr' () Def Var.Canonical Type.Canonical
20+
21+
22+data Def
23+ = Definition Facts Pattern.Optimized Expr
24+ deriving (Show)
25+
26+
27+data Facts = Facts
28+ { tailRecursionDetails :: Maybe String
29+ }
30+ deriving (Show)
31+
32+
33+dummyFacts :: Facts
34+dummyFacts =
35+ Facts Nothing
src/AST/Literal.hs+10−5
@@ -18,12 +18,17 @@ data Literal
1818
1919instance P.Pretty Literal where
2020 pretty _ _ literal =
21+ P.text (toString literal)
22+
23+
24+toString :: Literal -> String
25+toString literal =
2126 case literal of
22− IntNum n -> P.int n
23− FloatNum n -> P.double n
24− Chr c -> P.text . show $ c
25− Str s -> P.text . show $ s
26− Boolean bool -> P.text (show bool)
27+ IntNum n -> show n
28+ FloatNum n -> show n
29+ Chr c -> show c
30+ Str s -> show s
31+ Boolean bool -> show bool
2732
2833
2934data GLTipe
src/AST/Module.hs+10−5
@@ -2,8 +2,8 @@ module AST.Module
22 ( Interfaces
33 , Types, Aliases, ADTs
44 , AdtInfo, CanonicalAdt
5− , SourceModule, ValidModule, CanonicalModule
6− , Module(..), CanonicalBody(..)
5+ , SourceModule, ValidModule, CanonicalModule, Optimized
6+ , Module(..), Body(..)
77 , Header(..)
88 , Name, nameToString, nameIsNative
99 , Interface(..), toInterface
@@ -17,6 +17,7 @@ import qualified Data.Map as Map
1717
1818import qualified AST.Declaration as Decl
1919import qualified AST.Expression.Canonical as Canonical
20+import qualified AST.Expression.Optimized as Optimized
2021import qualified AST.Type as Type
2122import qualified AST.Variable as Var
2223import qualified Docs.AST as Docs
@@ -55,7 +56,11 @@ type ValidModule =
5556
5657
5758type CanonicalModule =
58− Module Docs.Centralized [Name] [Var.Value] CanonicalBody
59+ Module Docs.Centralized [Name] [Var.Value] (Body Canonical.Expr)
60+
61+
62+type Optimized =
63+ Module Docs.Centralized [Name] [Var.Value] (Body Optimized.Expr)
5964
6065
6166data Module docs imports exports body = Module
@@ -68,8 +73,8 @@ data Module docs imports exports body = Module
6873 }
6974
7075
71−data CanonicalBody = CanonicalBody
72− { program :: Canonical.Expr
76+data Body expr = Body
77+ { program :: expr
7378 , types :: Types
7479 , fixities :: [(Decl.Assoc, Int, String)]
7580 , aliases :: Aliases
src/AST/Pattern.hs+4
@@ -38,6 +38,10 @@ type CanonicalPattern =
3838 Pattern R.Region Var.Canonical
3939
4040
41+type Optimized =
42+ Pattern () Var.Canonical
43+
44+
4145list :: R.Position -> [RawPattern] -> RawPattern
4246list end patterns =
4347 case patterns of
src/Canonicalize.hs+11−6
@@ -8,7 +8,7 @@ import qualified Data.Set as Set
88import qualified Data.Traversable as T
99
1010import AST.Expression.General (Expr'(..), dummyLet)
11−import AST.Module (CanonicalBody(..))
11+import AST.Module (Body(..))
1212
1313import qualified AST.Declaration as D
1414import qualified AST.Expression.General as E
@@ -61,7 +61,7 @@ type AlmostCanonicalModule =
6161 Docs.Centralized
6262 ([Module.DefaultImport], [Module.UserImport])
6363 [Var.Value]
64− Module.CanonicalBody
64+ (Module.Body Canonical.Expr)
6565
6666
6767moduleHelp
@@ -89,11 +89,11 @@ moduleHelp interfaces modul@(Module.Module _ _ comment exports _ decls) =
8989 Setup.environment interfaces modul
9090 `Result.andThen` \env -> (,) env <$> T.traverse (declaration env) decls
9191
92− body :: [D.CanonicalDecl] -> Module.CanonicalBody
92+ body :: [D.CanonicalDecl] -> Module.Body Canonical.Expr
9393 body decls =
9494 let nakedDecls = map A.drop decls
9595 in
96− Module.CanonicalBody
96+ Module.Body
9797 { program =
9898 let expr = Decls.toExpr (Module.names modul) decls
9999 in
@@ -367,8 +367,10 @@ expression env (A.A region validExpr) =
367367 App func arg ->
368368 App <$> go func <*> go arg
369369
370− MultiIf branches ->
371− MultiIf <$> T.traverse go' branches
370+ MultiIf branches finally ->
371+ MultiIf
372+ <$> T.traverse go' branches
373+ <*> go finally
372374 where
373375 go' (condition, branch) =
374376 (,) <$> go condition <*> go branch
@@ -427,6 +429,9 @@ expression env (A.A region validExpr) =
427429 GLShader uid src tipe ->
428430 Result.ok (GLShader uid src tipe)
429431
432+ Crash details ->
433+ Result.ok (Crash details)
434+
430435
431436pattern
432437 :: Env.Environment
src/Canonicalize/Sort.hs+17−5
@@ -73,17 +73,26 @@ reorder (A.A ann expression) =
7373 App func arg ->
7474 App <$> reorder func <*> reorder arg
7575
76− MultiIf branches ->
77− MultiIf <$> mapM (\(cond,branch) -> (,) <$> reorder cond <*> reorder branch) branches
76+ MultiIf branches finally ->
77+ MultiIf
78+ <$> mapM (\(cond,branch) -> (,) <$> reorder cond <*> reorder branch) branches
79+ <*> reorder finally
7880
7981 Access record field ->
80− Access <$> reorder record <*> return field
82+ Access
83+ <$> reorder record
84+ <*> return field
8185
8286 Remove record field ->
83− Remove <$> reorder record <*> return field
87+ Remove
88+ <$> reorder record
89+ <*> return field
8490
8591 Insert record field expr ->
86− Insert <$> reorder record <*> return field <*> reorder expr
92+ Insert
93+ <$> reorder record
94+ <*> return field
95+ <*> reorder expr
8796
8897 Modify record fields ->
8998 Modify
@@ -109,6 +118,9 @@ reorder (A.A ann expression) =
109118 Task name expr tipe ->
110119 (\e -> Task name e tipe) <$> reorder expr
111120
121+ Crash _ ->
122+ return expression
123+
112124 -- Actually do some reordering
113125 Let defs body ->
114126 do body' <- reorder body
src/Compile.hs+3
@@ -5,6 +5,7 @@ import qualified Data.Map as Map
55import qualified AST.Module as Module
66import qualified Canonicalize
77import Elm.Utils ((|>))
8+import qualified Nitpick.PatternMatches as Nitpick
89import qualified Nitpick.TopLevelTypes as Nitpick
910import qualified Parse.Helpers as Parse
1011import qualified Parse.Parse as Parse
@@ -47,6 +48,8 @@ compile user projectName isRoot interfaces source =
4748 Result.mapError Error.Type $
4849 Nitpick.topLevelTypes types (Module.body validModule)
4950
51+ Nitpick.patternMatches interfaces canonicalModule
52+
5053 -- Add the real list of types
5154 let body = (Module.body canonicalModule) { Module.types = types }
5255
src/Elm/Compiler.hs+7−1
@@ -18,6 +18,7 @@ import qualified Elm.Compiler.Module as PublicModule
1818import qualified Elm.Compiler.Version as Version
1919import qualified Elm.Docs as Docs
2020import qualified Generate.JavaScript as JS
21+import qualified Optimize
2122import qualified Parse.Module as Parse
2223import qualified Parse.Parse as Parse
2324import qualified Reporting.Annotation as A
@@ -80,7 +81,12 @@ compile context source interfaces =
8081 (Result.Result (dealiaser, warnings) rawResult) =
8182 do modul <- Compile.compile user packageName isRoot unwrappedInterfaces source
8283 docs <- docsGen isExposed modul
83− return (Result docs (Module.toInterface modul) (JS.generate modul))
84+
85+ let interface = Module.toInterface modul
86+ let optModule = Optimize.optimize modul
87+ let javascript = JS.generate optModule
88+
89+ return (Result docs interface javascript)
8490 in
8591 ( maybe dummyDealiaser Dealiaser dealiaser
8692 , map Warning warnings
src/Generate/JavaScript.hs+602−268
@@ -1,17 +1,18 @@
11module Generate.JavaScript (generate) where
22
33import Control.Applicative ((<$>),(<*>))
4−import Control.Arrow (first,(***))
4+import Control.Arrow (first, second, (***))
55import Control.Monad.State
66import qualified Data.List as List
77import qualified Data.Map as Map
8+import qualified Data.Maybe as Maybe
89import qualified Data.Set as Set
910import Language.ECMAScript3.PrettyPrint
1011import Language.ECMAScript3.Syntax
1112
1213import AST.Module
13−import AST.Expression.General
14−import qualified AST.Expression.Canonical as Canonical
14+import AST.Expression.General as Expr
15+import qualified AST.Expression.Optimized as Opt
1516import qualified AST.Helpers as Help
1617import AST.Literal
1718import qualified AST.Module as Module
@@ -19,19 +20,22 @@ import qualified AST.Pattern as P
1920import qualified AST.Variable as Var
2021import Elm.Utils ((|>))
2122import Generate.JavaScript.Helpers as Help
22−import qualified Generate.Cases as Case
23+import qualified Generate.JavaScript.Crash as Crash
2324import qualified Generate.JavaScript.Port as Port
2425import qualified Generate.JavaScript.Variable as Var
26+import qualified Optimize.Cases as Case
2527import qualified Reporting.Annotation as A
26−import qualified Reporting.Region as R
28+import qualified Reporting.Crash as Crash
2729
2830
31+-- HELPERS
32+
2933internalImports :: Module.Name -> [VarDecl ()]
3034internalImports name =
3135 [ varDecl "_N" (obj ["Elm","Native"])
3236 , include "_U" "Utils"
3337 , include "_L" "List"
34− , varDecl Help.localModuleName (string (Module.nameToString name))
38+ , varDecl Crash.localModuleName (string (Module.nameToString name))
3539 ]
3640 where
3741 include :: String -> String -> VarDecl ()
@@ -59,51 +63,131 @@ literal lit =
5963 Boolean b -> BoolLit () b
6064
6165
62−expression :: Canonical.Expr -> State Int (Expression ())
63−expression (A.A region expr) =
66+-- CODE CHUNKS
67+
68+data Code
69+ = JsExpr (Expression ())
70+ | JsBlock [Statement ()]
71+
72+
73+jsExpr :: Expression () -> State Int Code
74+jsExpr exp =
75+ return (JsExpr exp)
76+
77+
78+jsBlock :: [Statement ()] -> State Int Code
79+jsBlock exp =
80+ return (JsBlock exp)
81+
82+
83+isBlock :: Code -> Bool
84+isBlock code =
85+ case code of
86+ JsBlock _ -> True
87+ JsExpr _ -> False
88+
89+
90+toStatementList :: Code -> [Statement ()]
91+toStatementList code =
92+ case code of
93+ JsExpr expr ->
94+ [ ret expr ]
95+ JsBlock stmts ->
96+ stmts
97+
98+
99+toStatement :: Code -> Statement ()
100+toStatement code =
101+ case code of
102+ JsExpr expr ->
103+ ret expr
104+ JsBlock [stmt] ->
105+ stmt
106+ JsBlock stmts ->
107+ BlockStmt () stmts
108+
109+
110+toExpr :: Code -> Expression ()
111+toExpr code =
112+ case code of
113+ JsExpr expr ->
114+ expr
115+ JsBlock stmts ->
116+ function [] stmts `call` []
117+
118+
119+-- TAIL CALL CONTEXT
120+
121+type TailCallContext =
122+ Maybe (String, [String], Label)
123+
124+
125+type Label =
126+ Id ()
127+
128+
129+-- EXPRESSIONS
130+
131+toJsExpr :: Opt.Expr -> State Int (Expression ())
132+toJsExpr =
133+ exprToJsExpr Nothing
134+
135+
136+toCode :: Opt.Expr -> State Int Code
137+toCode =
138+ exprToCode Nothing
139+
140+
141+exprToJsExpr :: TailCallContext -> Opt.Expr -> State Int (Expression ())
142+exprToJsExpr tcc canonicalExpr =
143+ toExpr <$> exprToCode tcc canonicalExpr
144+
145+
146+exprToCode :: TailCallContext -> Opt.Expr -> State Int Code
147+exprToCode tcc annotatedExpr@(A.A _ expr) =
64148 case expr of
65149 Var var ->
66− return $ Var.canonical var
150+ jsExpr $ Var.canonical var
67151
68152 Literal lit ->
69− return $ literal lit
153+ jsExpr $ literal lit
70154
71155 Range lo hi ->
72− do lo' <- expression lo
73− hi' <- expression hi
74− return $ _List "range" `call` [lo',hi']
156+ do lo' <- toJsExpr lo
157+ hi' <- toJsExpr hi
158+ jsExpr $ _List "range" `call` [lo',hi']
75159
76− Access e field ->
77− do e' <- expression e
78− return $ DotRef () e' (var (Var.varName field))
160+ Access record field ->
161+ do record' <- toJsExpr record
162+ jsExpr $ DotRef () record' (var (Var.varName field))
79163
80− Remove e field ->
81− do e' <- expression e
82− return $ _Utils "remove" `call` [ string (Var.varName field), e' ]
164+ Remove record field ->
165+ do record' <- toJsExpr record
166+ jsExpr $ _Utils "remove" `call` [ string (Var.varName field), record' ]
83167
84− Insert e field value ->
85− do value' <- expression value
86− e' <- expression e
87− return $ _Utils "insert" `call` [ string (Var.varName field), value', e' ]
168+ Insert record field value ->
169+ do value' <- toJsExpr value
170+ record' <- toJsExpr record
171+ jsExpr $ _Utils "insert" `call` [ string (Var.varName field), value', record' ]
88172
89− Modify e fields ->
90− do e' <- expression e
173+ Modify record fields ->
174+ do record' <- toJsExpr record
91175 fields' <-
92176 forM fields $ \(field, value) ->
93− do value' <- expression value
177+ do value' <- toJsExpr value
94178 return $ ArrayLit () [ string (Var.varName field), value' ]
95179
96− return $ _Utils "replace" `call` [ArrayLit () fields', e']
180+ jsExpr $ _Utils "replace" `call` [ArrayLit () fields', record']
97181
98182 Record fields ->
99183 do fields' <-
100184 forM fields $ \(field, e) ->
101− (,) (Var.varName field) <$> expression e
185+ (,) (Var.varName field) <$> toJsExpr e
102186
103187 let fieldMap =
104188 List.foldl' combine Map.empty fields'
105189
106− return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
190+ jsExpr $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
107191 where
108192 combine record (field, value) =
109193 Map.insertWith (++) field [value] record
@@ -115,201 +199,370 @@ expression (A.A region expr) =
115199 visible fs =
116200 map (first prop) (Map.toList (Map.map head fs))
117201
118− Binop op e1 e2 ->
119− binop region op e1 e2
202+ Binop op leftExpr rightExpr ->
203+ binop op leftExpr rightExpr
120204
121− Lambda pattern rawBody@(A.A ann _) ->
122− do (args, body) <- foldM depattern ([], innerBody) (reverse patterns)
123− body' <- expression body
124− return $
125− case length args < 2 || length args > 9 of
126− True -> foldr (==>) body' (map (:[]) args)
127− False -> ref ("F" ++ show (length args)) <| (args ==> body')
128− where
129− depattern (args, body) pattern =
130− case pattern of
131− A.A _ (P.Var x) ->
132− return (args ++ [ Var.varName x ], body)
133−
134− _ ->
135− do arg <- Case.newVar
136− return
137− ( args ++ [arg]
138− , A.A ann (Case (A.A ann (localVar arg)) [(pattern, body)])
139− )
140−
141− (patterns, innerBody) =
142− collect [pattern] rawBody
143−
144− collect patterns lexpr@(A.A _ expr) =
145− case expr of
146− Lambda p e -> collect (p:patterns) e
147− _ -> (patterns, lexpr)
148−
149− App e1 e2 ->
150− do func' <- expression func
151− args' <- mapM expression args
152− return $
153− case args' of
154− [arg] -> func' <| arg
155− _ | length args' <= 9 -> ref aN `call` (func':args')
156− | otherwise -> foldl1 (<|) (func':args')
157− where
158− aN = "A" ++ show (length args)
159− (func, args) = getArgs e1 [e2]
160− getArgs func args =
161− case func of
162− (A.A _ (App f arg)) -> getArgs f (arg : args)
163− _ -> (func, args)
164−
165− Let defs e ->
166− do let (defs',e') = flattenLets defs e
167− stmts <- concat <$> mapM definition defs'
168− exp <- expression e'
169− return $ function [] (stmts ++ [ ret exp ]) `call` []
170−
171− MultiIf branches ->
172− do branches' <- forM branches $ \(b,e) -> (,) <$> expression b <*> expression e
173− return $
174− case last branches of
175− (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), _) ->
176− safeIfs branches'
177− (A.A _ (Literal (Boolean True)), _) ->
178− safeIfs branches'
179− _ ->
180− ifs branches' (throw "badIf" region)
181− where
182− safeIfs branches = ifs (init branches) (snd (last branches))
183− ifs branches finally = foldr iff finally branches
184− iff (if', then') else' = CondExpr () if' then' else'
185−
186− Case e cases ->
187− do (tempVar,initialMatch) <- Case.toMatch cases
188− (revisedMatch, stmt) <-
189− case e of
190− A.A _ (Var (Var.Canonical Var.Local x)) ->
191− return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
192− _ ->
193− do e' <- expression e
194− return (initialMatch, [VarDeclStmt () [varDecl tempVar e']])
195− match' <- match region revisedMatch
196− return (function [] (stmt ++ match') `call` [])
197−
198− ExplicitList es ->
199− do es' <- mapM expression es
200− return $ _List "fromArray" <| ArrayLit () es'
201−
202− Data name es ->
203− do es' <- mapM expression es
204− return $ ObjectLit () (ctor : fields es')
205+ Lambda _ _ ->
206+ generateFunction (Expr.collectLambdas annotatedExpr)
207+
208+ App _ _ ->
209+ generateApplication tcc annotatedExpr
210+
211+ Let defs body ->
212+ generateLet tcc defs body
213+
214+ MultiIf branches finally ->
215+ generateIf tcc branches finally
216+
217+ Case expr branches ->
218+ generateCase tcc expr branches
219+
220+ ExplicitList elements ->
221+ do jsElements <- mapM toJsExpr elements
222+ jsExpr $ _List "fromArray" <| ArrayLit () jsElements
223+
224+ Data tag members ->
225+ do jsMembers <- mapM toJsExpr members
226+ jsExpr $ ObjectLit () (ctor : fields jsMembers)
205227 where
206− ctor = (prop "ctor", string name)
228+ ctor = (prop "ctor", string tag)
207229 fields =
208230 zipWith (\n e -> (prop ("_" ++ show n), e)) [ 0 :: Int .. ]
209231
210232 GLShader _uid src _tipe ->
211− return $ ObjectLit () [(PropString () "src", literal (Str src))]
233+ jsExpr $ ObjectLit () [(PropString () "src", literal (Str src))]
212234
213235 Port impl ->
214236 case impl of
215237 In name portType ->
216− return (Port.inbound name portType)
238+ jsExpr (Port.inbound name portType)
217239
218240 Out name expr portType ->
219− do expr' <- expression expr
220− return (Port.outbound name expr' portType)
241+ do expr' <- toJsExpr expr
242+ jsExpr (Port.outbound name expr' portType)
221243
222244 Task name expr portType ->
223− do expr' <- expression expr
224− return (Port.task name expr' portType)
225−
226−
227−definition :: Canonical.Def -> State Int [Statement ()]
228−definition (Canonical.Definition annPattern expr@(A.A region _) _) =
229− do expr' <- expression expr
230− let assign x = varDecl x expr'
231− let (A.A patternRegion pattern) = annPattern
232− case pattern of
233− P.Var x
234− | Help.isOp x ->
235− let op = LBracket () (ref "_op") (string x) in
236− return [ ExprStmt () $ AssignExpr () OpAssign op expr' ]
237−
238− | otherwise ->
239− return [ VarDeclStmt () [ assign (Var.varName x) ] ]
240−
241− P.Record fields ->
242− let setField f = varDecl f (obj ["$",f]) in
243− return [ VarDeclStmt () (assign "$" : map setField fields) ]
244−
245− P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
246− return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
247− where
248− vars = getVars patterns
249− getVars patterns =
250− case patterns of
251− A.A _ (P.Var x) : rest ->
252− (Var.varName x :) `fmap` getVars rest
253− [] ->
254− Just []
255− _ ->
256− Nothing
257−
258− decl x n = varDecl x (obj ["$","_" ++ show n])
259− setup vars
260− | Help.isTuple name = assign "$" : vars
261− | otherwise = assign "_raw" : safeAssign : vars
262−
263− safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
264− if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
265− exception = Help.throw "badCase" region
245+ do expr' <- toJsExpr expr
246+ jsExpr (Port.task name expr' portType)
247+
248+ Crash details ->
249+ jsExpr $ Crash.crash details
250+
251+
252+-- FUNCTIONS
253+
254+generateFunction :: ([P.Optimized], Opt.Expr) -> State Int Code
255+generateFunction (args, initialBody) =
256+ do (argVars, patternDefs) <- destructuredArgs args
257+ code <- generateLet Nothing patternDefs initialBody
258+ jsExpr (generateFunctionWithArity argVars code)
259+
260+
261+generateTailFunction :: String -> ([P.Optimized], Opt.Expr) -> State Int (Expression ())
262+generateTailFunction name (args, initialBody) =
263+ do (argVars, patternDefs) <- destructuredArgs args
264+ label <- Id () <$> Case.newVar
265+ code <- generateLet (Just (name, argVars, label)) patternDefs initialBody
266+ let whileLoop =
267+ JsBlock [ LabelledStmt () label (WhileStmt () (BoolLit () True) (toStatement code)) ]
268+ return (generateFunctionWithArity argVars whileLoop)
269+
270+
271+generateFunctionWithArity :: [String] -> Code -> Expression ()
272+generateFunctionWithArity args code =
273+ let
274+ arity = length args
275+ in
276+ if 2 <= arity && arity <= 9 then
277+ let
278+ fN = "F" ++ show arity
279+ in
280+ ref fN <| function args (toStatementList code)
281+ else
282+ let
283+ (lastArg:otherArgs) = reverse args
284+ innerBody = function [lastArg] (toStatementList code)
285+ in
286+ foldl (\body arg -> function [arg] [ret body]) innerBody otherArgs
287+
288+
289+destructuredArgs :: [P.Optimized] -> State Int ([String], [Opt.Def])
290+destructuredArgs args =
291+ do (argVars, maybePatternDefs) <- unzip <$> mapM destructPattern args
292+ return (argVars, Maybe.catMaybes maybePatternDefs)
293+
294+
295+destructPattern :: P.Optimized -> State Int (String, Maybe Opt.Def)
296+destructPattern pattern@(A.A ann pat) =
297+ case pat of
298+ P.Var x ->
299+ return (Var.varName x, Nothing)
300+
301+ _ ->
302+ do arg <- Case.newVar
303+ return
304+ ( arg
305+ , Just $ Opt.Definition Opt.dummyFacts pattern (A.A ann (localVar arg))
306+ )
307+
308+
309+-- APPLICATIONS
310+
311+generateApplication :: TailCallContext -> Opt.Expr -> State Int Code
312+generateApplication tcc expr =
313+ let
314+ (func:args) =
315+ Expr.collectApps expr
316+
317+ arity =
318+ length args
319+
320+ reassign name tempName =
321+ ExprStmt () (AssignExpr () OpAssign (LVar () name) (ref tempName))
322+
323+ aN =
324+ "A" ++ show arity
325+ in
326+ case isTailCall tcc func arity of
327+ Just (argNames, label) ->
328+ do args' <- mapM toJsExpr args
329+ tempNames <- mapM (\_ -> Case.newVar) args
330+ jsBlock $
331+ VarDeclStmt () (zipWith varDecl tempNames args')
332+ : zipWith reassign argNames tempNames
333+ ++ [ContinueStmt () (Just label)]
266334
267− _ ->
268− do defs' <- concat <$> mapM toDef vars
269− return (VarDeclStmt () [assign "_"] : defs')
270− where
271− vars = P.boundVarList annPattern
272− mkVar = A.A region . localVar
273− toDef y =
274− let expr = A.A region $ Case (mkVar "_") [(annPattern, mkVar y)]
275− pat = A.A patternRegion (P.Var y)
276− in
277− definition (Canonical.Definition pat expr Nothing)
278−
279−
280−match :: R.Region -> Case.Match -> State Int [Statement ()]
281−match region mtch =
335+ _ ->
336+ do func' <- toJsExpr func
337+ args' <- mapM toJsExpr args
338+ jsExpr $
339+ if 2 <= arity && arity <= 9 then
340+ ref aN `call` (func':args')
341+ else
342+ foldl1 (<|) (func':args')
343+
344+
345+isTailCall :: TailCallContext -> Opt.Expr -> Int -> Maybe ([String], Label)
346+isTailCall tcc (A.A _ func) arity =
347+ case (func, tcc) of
348+ (Var (Var.Canonical Var.Local name), Just (tcName, tcArgs, tcLabel)) ->
349+ if name == tcName && length tcArgs == arity then
350+ Just (tcArgs, tcLabel)
351+ else
352+ Nothing
353+
354+ _ ->
355+ Nothing
356+
357+
358+-- LET EXPRESSIONS
359+
360+generateLet
361+ :: TailCallContext
362+ -> [Opt.Def]
363+ -> Opt.Expr
364+ -> State Int Code
365+generateLet tcc givenDefs givenBody =
366+ do let (defs, body) = flattenLets givenDefs givenBody
367+ stmts <- concat <$> mapM defToStatements defs
368+ code <- exprToCode tcc body
369+ jsBlock (stmts ++ toStatementList code)
370+
371+
372+-- GENERATE IFS
373+
374+generateIf :: TailCallContext -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> State Int Code
375+generateIf tcc givenBranches givenFinally =
376+ let
377+ (branches, finally) =
378+ crushIfs givenBranches givenFinally
379+
380+ convertBranch (condition, expr) =
381+ (,) <$> toJsExpr condition <*> exprToCode tcc expr
382+
383+ ifExpression (condition, branch) otherwise =
384+ CondExpr () condition branch otherwise
385+
386+ ifStatement (condition, branch) otherwise =
387+ IfStmt () condition branch otherwise
388+ in
389+ do jsBranches <- mapM convertBranch branches
390+ jsFinally <- exprToCode tcc finally
391+
392+ if any isBlock (jsFinally : map snd jsBranches)
393+ then
394+ jsBlock [ foldr ifStatement (toStatement jsFinally) (map (second toStatement) jsBranches) ]
395+ else
396+ jsExpr (foldr ifExpression (toExpr jsFinally) (map (second toExpr) jsBranches))
397+
398+
399+crushIfs
400+ :: [(Opt.Expr, Opt.Expr)]
401+ -> Opt.Expr
402+ -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
403+crushIfs branches finally =
404+ crushIfsHelp [] branches finally
405+
406+
407+crushIfsHelp
408+ :: [(Opt.Expr, Opt.Expr)]
409+ -> [(Opt.Expr, Opt.Expr)]
410+ -> Opt.Expr
411+ -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
412+crushIfsHelp visitedBranches unvisitedBranches finally =
413+ case unvisitedBranches of
414+ [] ->
415+ case finally of
416+ A.A _ (MultiIf subBranches subFinally) ->
417+ crushIfsHelp visitedBranches subBranches subFinally
418+
419+ _ ->
420+ (reverse visitedBranches, finally)
421+
422+ (A.A _ (Literal (Boolean True)), branch) : _ ->
423+ crushIfsHelp visitedBranches [] branch
424+
425+ (A.A _ (Var (Var.Canonical (Var.Module ["Basics"]) "otherwise")), branch) : _ ->
426+ crushIfsHelp visitedBranches [] branch
427+
428+ visiting : unvisited ->
429+ crushIfsHelp (visiting : visitedBranches) unvisited finally
430+
431+
432+
433+-- DEFINITIONS
434+
435+defToStatements :: Opt.Def -> State Int [Statement ()]
436+defToStatements (Opt.Definition facts pattern expr) =
437+ case Opt.tailRecursionDetails facts of
438+ Just name ->
439+ do func <- generateTailFunction name (Expr.collectLambdas expr)
440+ return [ VarDeclStmt () [ varDecl name func ] ]
441+
442+ Nothing ->
443+ defToStatementsHelp facts pattern =<< toJsExpr expr
444+
445+
446+defToStatementsHelp
447+ :: Opt.Facts
448+ -> P.Optimized
449+ -> Expression ()
450+ -> State Int [Statement ()]
451+defToStatementsHelp facts annPattern@(A.A _ pattern) jsExpr =
452+ let
453+ define x =
454+ varDecl x jsExpr
455+ in
456+ case pattern of
457+ P.Var x ->
458+ if Help.isOp x then
459+ let
460+ op = LBracket () (ref "_op") (string x)
461+ in
462+ return [ ExprStmt () $ AssignExpr () OpAssign op jsExpr ]
463+ else
464+ return [ VarDeclStmt () [ define (Var.varName x) ] ]
465+
466+ P.Record fields ->
467+ let
468+ setField name =
469+ varDecl name (obj ["$",name])
470+ in
471+ return [ VarDeclStmt () (define "$" : map setField fields) ]
472+
473+ P.Data (Var.Canonical _ name) patterns | vars /= Nothing ->
474+ return [ VarDeclStmt () (setup (zipWith decl (maybe [] id vars) [0..])) ]
475+ where
476+ vars = getVars patterns
477+ getVars patterns =
478+ case patterns of
479+ A.A _ (P.Var x) : rest ->
480+ (Var.varName x :) `fmap` getVars rest
481+ [] ->
482+ Just []
483+ _ ->
484+ Nothing
485+
486+ decl x n = varDecl x (obj ["$","_" ++ show n])
487+ setup vars
488+ | Help.isTuple name = define "$" : vars
489+ | otherwise = define "_raw" : safeAssign : vars
490+
491+ safeAssign = varDecl "$" (CondExpr () if' (ref "_raw") exception)
492+ if' = InfixExpr () OpStrictEq (obj ["_raw","ctor"]) (string name)
493+ exception = Crash.crash (Crash.IncompletePatternMatch (error "TODO"))
494+
495+ _ ->
496+ do defs' <- concat <$> mapM toDef vars
497+ return (VarDeclStmt () [define "_"] : defs')
498+ where
499+ vars = P.boundVarList annPattern
500+ mkVar = A.A () . localVar
501+ toDef y =
502+ let expr = A.A () $ Case (mkVar "_") [(annPattern, mkVar y)]
503+ pat = A.A () (P.Var y)
504+ in
505+ defToStatements (Opt.Definition facts pat expr)
506+
507+
508+-- CASE EXPRESSIONS
509+
510+generateCase :: TailCallContext -> Opt.Expr -> [(P.Optimized, Opt.Expr)] -> State Int Code
511+generateCase tcc expr branches =
512+ do (tempVar, initialMatch) <-
513+ Case.toMatch branches
514+
515+ (revisedMatch, stmt) <-
516+ case expr of
517+ A.A _ (Var (Var.Canonical Var.Local x)) ->
518+ return (Case.matchSubst [(tempVar, Var.varName x)] initialMatch, [])
519+ _ ->
520+ do expr' <- toJsExpr expr
521+ return (initialMatch, [VarDeclStmt () [varDecl tempVar expr']])
522+
523+ match' <- match tcc revisedMatch
524+
525+ jsBlock (stmt ++ match')
526+
527+
528+match :: TailCallContext -> Case.Match -> State Int [Statement ()]
529+match tcc mtch =
282530 case mtch of
283531 Case.Match name clauses mtch' ->
284− do (isChars, clauses') <- unzip <$> mapM (clause region name) clauses
285− mtch'' <- match region mtch'
532+ do (isChars, clauses') <- unzip <$> mapM (clause tcc name) clauses
533+ mtch'' <- match tcc mtch'
286534 return (SwitchStmt () (format isChars (access name)) clauses' : mtch'')
287535 where
288536 isLiteral p =
289537 case p of
290− Case.Clause (Right _) _ _ -> True
291− _ -> False
292−
293− access name
294− | any isLiteral clauses = obj [name]
295− | otherwise = obj (Help.splitDots name ++ ["ctor"])
296−
297− format isChars e
298− | or isChars = InfixExpr () OpAdd e (string "")
299− | otherwise = e
538+ Case.Clause (Right _) _ _ ->
539+ True
540+ _ ->
541+ False
542+
543+ access name =
544+ if any isLiteral clauses then
545+ obj [name]
546+ else
547+ obj (Help.splitDots name ++ ["ctor"])
548+
549+ format isChars expr =
550+ if or isChars then
551+ InfixExpr () OpAdd expr (string "")
552+ else
553+ expr
300554
301555 Case.Fail ->
302− return [ ExprStmt () (Help.throw "badCase" region) ]
556+ return [ ExprStmt () (Crash.crash (Crash.IncompletePatternMatch (error "TODO"))) ]
303557
304558 Case.Break ->
305559 return [BreakStmt () Nothing]
306560
307− Case.Other e ->
308− do e' <- expression e
309− return [ ret e' ]
561+ Case.Other expr ->
562+ toStatementList <$> exprToCode tcc expr
310563
311564 Case.Seq ms ->
312− concat <$> mapM (match region) (dropEnd [] ms)
565+ concat <$> mapM (match tcc) (dropEnd [] ms)
313566 where
314567 dropEnd acc [] = acc
315568 dropEnd acc (m:ms) =
@@ -318,9 +571,10 @@ match region mtch =
318571 _ -> dropEnd (acc ++ [m]) ms
319572
320573
321−clause :: R.Region -> String -> Case.Clause -> State Int (Bool, CaseClause ())
322−clause region variable (Case.Clause value vars mtch) =
323− (,) isChar . CaseClause () pattern <$> match region (Case.matchSubst (zip vars vars') mtch)
574+clause :: TailCallContext -> String -> Case.Clause -> State Int (Bool, CaseClause ())
575+clause tcc variable (Case.Clause value vars mtch) =
576+ do statemens <- match tcc (Case.matchSubst (zip vars vars') mtch)
577+ return ((,) isChar (CaseClause () pattern statemens))
324578 where
325579 vars' =
326580 map (\n -> variable ++ "._" ++ show n) [0..]
@@ -337,14 +591,14 @@ clause region variable (Case.Clause value vars mtch) =
337591 string name
338592
339593
340−flattenLets :: [Canonical.Def] -> Canonical.Expr -> ([Canonical.Def], Canonical.Expr)
594+flattenLets :: [Opt.Def] -> Opt.Expr -> ([Opt.Def], Opt.Expr)
341595flattenLets defs lexpr@(A.A _ expr) =
342596 case expr of
343597 Let ds body -> flattenLets (defs ++ ds) body
344598 _ -> (defs, lexpr)
345599
346600
347−generate :: Module.CanonicalModule -> String
601+generate :: Module.Optimized -> String
348602generate modul =
349603 show . prettyPrint $ setup "Elm" (names ++ ["make"]) ++
350604 [ assign ("Elm" : names ++ ["make"]) (function [localRuntime] programStmts) ]
@@ -393,7 +647,7 @@ generate modul =
393647 Module.program (Module.body modul)
394648 |> flattenLets []
395649 |> fst
396− |> mapM definition
650+ |> mapM defToStatements
397651
398652 setup namespace path =
399653 map create paths
@@ -430,63 +684,104 @@ generate modul =
430684 AssignExpr () OpAssign (LDot () (obj (init path)) (last path)) expr
431685
432686
687+-- BINARY OPERATORS
688+
433689binop
434− :: R.Region
435− -> Var.Canonical
436− -> Canonical.Expr
437− -> Canonical.Expr
438− -> State Int (Expression ())
439−binop region func@(Var.Canonical home op) e1 e2 =
440− case (home, op) of
441− (Var.Module ["Basics"], ">>") ->
442− do es <- mapM expression (collectLeftAssoc [e2] e1)
443− return $ ["$"] ==> List.foldl' (\e f -> f <| e) (ref "$") es
444−
445− (Var.Module ["Basics"], "<<") ->
446− do es <- mapM expression (e1 : collectRightAssoc [] e2)
447− return $ ["$"] ==> foldr (<|) (ref "$") es
448−
449− (Var.Module ["Basics"], "<|") ->
450− do e2' <- expression e2
451− es <- mapM expression (collectRightAssoc [] e1)
452− return $ foldr (<|) e2' es
453−
454− (Var.BuiltIn, "::") ->
455− expression (A.A region (Data "::" [e1,e2]))
456−
457− (Var.Module ["Basics"], _) ->
458− do e1' <- expression e1
459− e2' <- expression e2
460− case Map.lookup op basicOps of
461− Just f ->
462− return (f e1' e2')
463−
464− Nothing ->
465− return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
690+ :: Var.Canonical
691+ -> Opt.Expr
692+ -> Opt.Expr
693+ -> State Int Code
694+binop func left right
695+ | func == Var.Canonical Var.BuiltIn "::" =
696+ toCode (A.A () (Data "::" [left,right]))
466697
467− _ ->
468− do e1' <- expression e1
469− e2' <- expression e2
470− return (ref "A2" `call` [ Var.canonical func, e1', e2' ])
698+ | func == forwardCompose =
699+ compose (collectLeftAssoc forwardCompose left right)
471700
472− where
473− collectRightAssoc es e =
474− case e of
475− A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) "<<") e1 e2) ->
476− collectRightAssoc (es ++ [e1]) e2
477− _ -> es ++ [e]
478−
479− collectLeftAssoc es e =
480− case e of
481− A.A _ (Binop (Var.Canonical (Var.Module ["Basics"]) ">>") e1 e2) ->
482− collectLeftAssoc (e2 : es) e1
483− _ -> e : es
484−
485− basicOps =
486− Map.fromList (infixOps ++ specialOps)
487−
488− infixOps =
489− let infixOp str op = (str, InfixExpr () op) in
701+ | func == backwardCompose =
702+ compose (collectRightAssoc backwardCompose left right)
703+
704+ | func == forwardApply =
705+ pipe (collectLeftAssoc forwardApply left right)
706+
707+ | func == backwardApply =
708+ pipe (collectRightAssoc backwardApply left right)
709+
710+ | otherwise =
711+ do jsLeft <- toJsExpr left
712+ jsRight <- toJsExpr right
713+ jsExpr (makeExpr func jsLeft jsRight)
714+
715+
716+-- CONTROL FLOW OPERATOR HELPERS
717+
718+apply :: Expression () -> Expression () -> Expression ()
719+apply arg func =
720+ func <| arg
721+
722+
723+compose :: [Opt.Expr] -> State Int Code
724+compose functions =
725+ do jsFunctions <- mapM toJsExpr functions
726+ jsExpr $ ["$"] ==> List.foldl' apply (ref "$") jsFunctions
727+
728+
729+pipe :: [Opt.Expr] -> State Int Code
730+pipe expressions =
731+ do (value:functions) <- mapM toJsExpr expressions
732+ jsExpr $ List.foldl' apply value functions
733+
734+
735+forwardCompose :: Var.Canonical
736+forwardCompose =
737+ inBasics ">>"
738+
739+
740+backwardCompose :: Var.Canonical
741+backwardCompose =
742+ inBasics "<<"
743+
744+
745+forwardApply :: Var.Canonical
746+forwardApply =
747+ inBasics "|>"
748+
749+
750+backwardApply :: Var.Canonical
751+backwardApply =
752+ inBasics "<|"
753+
754+
755+inBasics :: String -> Var.Canonical
756+inBasics name =
757+ Var.Canonical (Var.Module ["Basics"]) name
758+
759+
760+-- BINARY OPERATOR HELPERS
761+
762+makeExpr :: Var.Canonical -> (Expression () -> Expression () -> Expression ())
763+makeExpr qualifiedOp@(Var.Canonical home op) =
764+ let
765+ simpleMake left right =
766+ ref "A2" `call` [ Var.canonical qualifiedOp, left, right ]
767+ in
768+ if home == Var.Module ["Basics"] then
769+ Map.findWithDefault simpleMake op basicOps
770+ else
771+ simpleMake
772+
773+
774+basicOps :: Map.Map String (Expression () -> Expression () -> Expression ())
775+basicOps =
776+ Map.fromList (infixOps ++ specialOps)
777+
778+
779+infixOps :: [(String, Expression () -> Expression () -> Expression ())]
780+infixOps =
781+ let
782+ infixOp str op =
783+ (str, InfixExpr () op)
784+ in
490785 [ infixOp "+" OpAdd
491786 , infixOp "-" OpSub
492787 , infixOp "*" OpMul
@@ -495,17 +790,56 @@ binop region func@(Var.Canonical home op) e1 e2 =
495790 , infixOp "||" OpLOr
496791 ]
497792
498− specialOps =
499− [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
500− , (,) "|>" $ flip (<|)
501− , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
502− , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
503− , (,) "<" $ cmp OpLT 0
504− , (,) ">" $ cmp OpGT 0
505− , (,) "<=" $ cmp OpLT 1
506− , (,) ">=" $ cmp OpGT (-1)
507− , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
508− ]
509793
510− cmp op n a b =
511− InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
794+specialOps :: [(String, Expression () -> Expression () -> Expression ())]
795+specialOps =
796+ [ (,) "^" $ \a b -> obj ["Math","pow"] `call` [a,b]
797+ , (,) "|>" $ flip (<|)
798+ , (,) "==" $ \a b -> _Utils "eq" `call` [a,b]
799+ , (,) "/=" $ \a b -> PrefixExpr () PrefixLNot (_Utils "eq" `call` [a,b])
800+ , (,) "<" $ cmp OpLT 0
801+ , (,) ">" $ cmp OpGT 0
802+ , (,) "<=" $ cmp OpLT 1
803+ , (,) ">=" $ cmp OpGT (-1)
804+ , (,) "//" $ \a b -> InfixExpr () OpBOr (InfixExpr () OpDiv a b) (IntLit () 0)
805+ ]
806+
807+
808+cmp :: InfixOp -> Int -> Expression () -> Expression () -> Expression ()
809+cmp op n a b =
810+ InfixExpr () op (_Utils "cmp" `call` [a,b]) (IntLit () n)
811+
812+
813+-- BINARY OPERATOR COLLECTORS
814+
815+-- (h << g << f) becomes [f, g, h] which is the order you want for doing stuff
816+collectRightAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
817+collectRightAssoc desiredOp left right =
818+ collectRightAssocHelp desiredOp [left] right
819+
820+
821+collectRightAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
822+collectRightAssocHelp desiredOp exprList expr =
823+ case expr of
824+ A.A _ (Binop op left right) | op == desiredOp ->
825+ collectRightAssocHelp desiredOp (left : exprList) right
826+
827+ _ ->
828+ expr : exprList
829+
830+
831+-- (f >> g >> h) becomes [f, g, h] which is the order you want for doing stuff
832+collectLeftAssoc :: Var.Canonical -> Opt.Expr -> Opt.Expr -> [Opt.Expr]
833+collectLeftAssoc desiredOp left right =
834+ collectLeftAssocHelp desiredOp [right] left
835+
836+
837+collectLeftAssocHelp :: Var.Canonical -> [Opt.Expr] -> Opt.Expr -> [Opt.Expr]
838+collectLeftAssocHelp desiredOp exprList expr =
839+ case expr of
840+ A.A _ (Binop op left right) | op == desiredOp ->
841+ collectLeftAssocHelp desiredOp (right : exprList) left
842+
843+ _ ->
844+ expr : exprList
845+
src/Generate/JavaScript/Crash.hsnew file+32
@@ -0,0 +1,32 @@
1+module Generate.JavaScript.Crash where
2+
3+import Language.ECMAScript3.Syntax (Expression)
4+
5+import Generate.JavaScript.Helpers (call, obj, ref, string)
6+import qualified Reporting.Crash as Crash
7+import qualified Reporting.Region as R
8+
9+
10+localModuleName :: String
11+localModuleName =
12+ "$moduleName"
13+
14+
15+crash :: Crash.Details -> Expression ()
16+crash details =
17+ case details of
18+ Crash.IncompleteMultiIf region ->
19+ obj ["_U","badIf"]
20+ `call`
21+ [ ref localModuleName
22+ , string (R.toString region)
23+ ]
24+
25+ Crash.IncompletePatternMatch _region ->
26+ obj ["_U","badCase"]
27+ `call`
28+ [ ref localModuleName
29+ , string "bugs in reporting the exact location right now" -- TODO
30+ ]
31+
32+
src/Generate/JavaScript/Helpers.hs−19
@@ -2,8 +2,6 @@ module Generate.JavaScript.Helpers where
22
33import Language.ECMAScript3.Syntax
44
5−import qualified Reporting.Region as R
6−
75
86localRuntime :: String
97localRuntime =
@@ -52,23 +50,6 @@ obj vars =
5250 [] -> error "dotSep must be called on a non-empty list of variables"
5351
5452
55−-- Specific Utilities
56−
57−localModuleName :: String
58−localModuleName =
59− "$moduleName"
60−
61−
62−throw :: String -> R.Region -> Expression ()
63−throw kind region =
64− let args =
65− [ ref localModuleName
66− , string (R.toString region)
67− ]
68− in
69− obj ["_U",kind] `call` args
70−
71−
7253-- Function Calls
7354
7455(<|) :: Expression () -> Expression () -> Expression ()
src/Nitpick/Pattern.hsnew file+71
@@ -0,0 +1,71 @@
1+module Nitpick.Pattern where
2+
3+import qualified Data.List as List
4+import qualified Data.Set as Set
5+
6+import qualified AST.Literal as L
7+import qualified AST.Pattern as P
8+import qualified AST.Variable as Var
9+import qualified Reporting.Annotation as A
10+
11+
12+data Pattern
13+ = Data Var.Canonical [Pattern]
14+ | Record [String]
15+ | Alias String Pattern
16+ | Var String
17+ | Anything
18+ | Literal L.Literal
19+ | AnythingBut (Set.Set L.Literal)
20+ deriving (Show, Eq)
21+
22+
23+fromCanonicalPattern :: P.CanonicalPattern -> Pattern
24+fromCanonicalPattern (A.A _ pattern) =
25+ case pattern of
26+ P.Data tag patterns ->
27+ Data tag (map fromCanonicalPattern patterns)
28+
29+ P.Record fields ->
30+ Record fields
31+
32+ P.Alias name pattern ->
33+ Alias name (fromCanonicalPattern pattern)
34+
35+ P.Var name ->
36+ Var name
37+
38+ P.Anything ->
39+ Anything
40+
41+ P.Literal literal ->
42+ Literal literal
43+
44+
45+-- TO STRING
46+
47+toString :: Pattern -> String
48+toString pattern =
49+ case pattern of
50+ Data tag args ->
51+ List.intercalate " " (Var.toString tag : map toString args)
52+
53+ Record fields ->
54+ "{ " ++ List.intercalate ", " fields ++ " }"
55+
56+ Alias alias realPattern ->
57+ toString realPattern ++ " as " ++ alias
58+
59+ Var name ->
60+ name
61+
62+ Anything ->
63+ "_"
64+
65+ Literal literal ->
66+ L.toString literal
67+
68+ AnythingBut literalSet ->
69+ List.intercalate " " ("<values besides:" : map L.toString (Set.toList literalSet))
70+
71+
src/Nitpick/PatternMatches.hsnew file+370
@@ -0,0 +1,370 @@
1+module Nitpick.PatternMatches (patternMatches) where
2+
3+import Control.Applicative ((<$>), (<*))
4+import Control.Arrow (second)
5+import qualified Data.Foldable as F
6+import qualified Data.Map as Map
7+import qualified Data.Maybe as Maybe
8+import qualified Data.Set as Set
9+
10+import qualified AST.Expression.General as E
11+import qualified AST.Expression.Canonical as Canonical
12+import qualified AST.Helpers as Help
13+import qualified AST.Module as Module
14+import qualified AST.Pattern as Pattern
15+import qualified AST.Variable as Var
16+import Elm.Utils ((|>))
17+import Nitpick.Pattern (Pattern(..), fromCanonicalPattern)
18+import qualified Reporting.Annotation as A
19+import qualified Reporting.Region as Region
20+import qualified Reporting.Result as Result
21+import qualified Reporting.Warning as Warning
22+
23+
24+patternMatches
25+ :: Module.Interfaces
26+ -> Module.CanonicalModule
27+ -> Result.Result Warning.Warning e ()
28+patternMatches interfaces modul =
29+ let
30+ name = Module.names modul
31+ body = Module.body modul
32+ in
33+ checkExpression
34+ (toTagDict interfaces name (Module.datatypes body))
35+ (Module.program body)
36+
37+
38+-- TAG DICT
39+
40+type TagDict =
41+ Map.Map Var.Home (Map.Map Tag [(Tag, Int)])
42+
43+
44+type Tag = String
45+
46+
47+toTagDict :: Module.Interfaces -> Module.Name -> Module.ADTs -> TagDict
48+toTagDict interfaces localName localAdts =
49+ let
50+ listTags =
51+ [ ("::", 2), ("[]", 0) ]
52+
53+ builtinDict =
54+ Map.singleton Var.BuiltIn $
55+ Map.fromList
56+ [ ("::", listTags)
57+ , ("[]", listTags)
58+ ]
59+
60+ interfaceDict =
61+ interfaces
62+ |> Map.map (toTagMapping . Module.iAdts)
63+ |> Map.mapKeysMonotonic Var.Module
64+ |> Map.insert (Var.Module localName) (toTagMapping localAdts)
65+ in
66+ Map.union builtinDict interfaceDict
67+
68+
69+toTagMapping :: Module.ADTs -> Map.Map Tag ([(Tag,Int)])
70+toTagMapping adts =
71+ let
72+ toTagAndArity (_tvars, tagInfoList) =
73+ let
74+ info = map (\(tag, args) -> (tag, length args)) tagInfoList
75+ in
76+ map (second (const info)) tagInfoList
77+ in
78+ Map.elems adts
79+ |> concatMap toTagAndArity
80+ |> Map.fromList
81+
82+
83+lookupOtherTags :: Var.Canonical -> TagDict -> [(Tag, Int)]
84+lookupOtherTags (Var.Canonical home name) tagDict =
85+ case Map.lookup name =<< Map.lookup home tagDict of
86+ Just otherTags ->
87+ otherTags
88+
89+ Nothing ->
90+ if Help.isTuple name then
91+ [(name, read (drop 6 name))]
92+ else
93+ error
94+ "Since the Nitpick phase happens after canonicalization and type \
95+ \inference, it is impossible that a pattern in a case cannot be \
96+ \found."
97+
98+
99+-- CHECK EXPRESSIONS
100+
101+checkExpression
102+ :: TagDict
103+ -> Canonical.Expr
104+ -> Result.Result Warning.Warning e ()
105+checkExpression tagDict (A.A region expression) =
106+ let
107+ go =
108+ checkExpression tagDict
109+
110+ go2 a b =
111+ go a <* go b
112+
113+ goPattern =
114+ checkPatterns tagDict region
115+ in
116+ case expression of
117+ E.Literal _ ->
118+ Result.ok ()
119+
120+ E.Var _ ->
121+ Result.ok ()
122+
123+ E.Range low high ->
124+ go2 low high
125+
126+ E.ExplicitList listExprs ->
127+ F.traverse_ go listExprs
128+
129+ E.Binop _ leftExpr rightExpr ->
130+ go2 leftExpr rightExpr
131+
132+ E.Lambda pattern body ->
133+ goPattern [pattern]
134+ <* go body
135+
136+ E.App func arg ->
137+ go2 func arg
138+
139+ E.MultiIf branches finally ->
140+ F.traverse_ (uncurry go2) branches
141+ <* go finally
142+
143+ E.Let defs body ->
144+ go body
145+ <* F.traverse_ goDef defs
146+ where
147+ goDef (Canonical.Definition pattern expr _) =
148+ goPattern [pattern]
149+ <* go expr
150+
151+ E.Case expr branches ->
152+ go expr
153+ <* goPattern (map fst branches)
154+ <* F.traverse_ (go . snd) branches
155+
156+ E.Data _ctor exprs ->
157+ F.traverse_ go exprs
158+
159+ E.Access record _field ->
160+ go record
161+
162+ E.Remove record _field ->
163+ go record
164+
165+ E.Insert record _field value ->
166+ go2 record value
167+
168+ E.Modify record fields ->
169+ go record
170+ <* F.traverse_ (go . snd) fields
171+
172+ E.Record fields ->
173+ F.traverse_ (go . snd) fields
174+
175+ E.Port impl ->
176+ case impl of
177+ E.In _ _ ->
178+ Result.ok ()
179+
180+ E.Out _ expr _ ->
181+ go expr
182+
183+ E.Task _ expr _ ->
184+ go expr
185+
186+ E.GLShader _ _ _ ->
187+ Result.ok ()
188+
189+ E.Crash _ ->
190+ Result.ok ()
191+
192+
193+-- CHECK PATTERNS
194+
195+checkPatterns
196+ :: TagDict
197+ -> Region.Region
198+ -> [Pattern.CanonicalPattern]
199+ -> Result.Result Warning.Warning e ()
200+checkPatterns tagDict region patterns =
201+ checkPatternsHelp tagDict region [Anything] patterns
202+
203+
204+checkPatternsHelp
205+ :: TagDict
206+ -> Region.Region
207+ -> [Pattern]
208+ -> [Pattern.CanonicalPattern]
209+ -> Result.Result Warning.Warning e ()
210+checkPatternsHelp tagDict region unhandled patterns =
211+ case (unhandled, patterns) of
212+ ([], []) ->
213+ return ()
214+
215+ (_:_, []) ->
216+ Result.warn region (Warning.InexhaustivePatternMatch unhandled)
217+
218+ (_, pattern@(A.A localRegion _) : remainingPatterns) ->
219+ do newUnhandled <- filterPatterns tagDict localRegion pattern unhandled
220+ checkPatternsHelp tagDict region newUnhandled remainingPatterns
221+
222+
223+filterPatterns
224+ :: TagDict
225+ -> Region.Region
226+ -> Pattern.CanonicalPattern
227+ -> [Pattern]
228+ -> Result.Result Warning.Warning e [Pattern]
229+filterPatterns tagDict region pattern unhandled =
230+ let
231+ nitPattern =
232+ fromCanonicalPattern pattern
233+
234+ noIntersection pat =
235+ intersection pat nitPattern == Nothing
236+ in
237+ if all noIntersection unhandled then
238+ do Result.warn region Warning.RedundantPatternMatch
239+ return unhandled
240+ else
241+ do let complementPatterns = complement tagDict nitPattern
242+ return $
243+ concatMap
244+ (\p -> Maybe.mapMaybe (intersection p) complementPatterns)
245+ unhandled
246+
247+
248+-- PATTERN INTERSECTION
249+
250+intersection :: Pattern -> Pattern -> Maybe Pattern
251+intersection pattern1 pattern2 =
252+ case (pattern1, pattern2) of
253+ (Alias _ pattern1', _) ->
254+ intersection pattern1' pattern2
255+
256+ (_, Alias _ pattern2') ->
257+ intersection pattern1 pattern2'
258+
259+ (Anything, _) ->
260+ Just pattern2
261+
262+ (Var _, _) ->
263+ Just pattern2
264+
265+ (_, Anything) ->
266+ Just pattern1
267+
268+ (_, Var _) ->
269+ Just pattern1
270+
271+ (Data ctor1 args1, Data ctor2 args2) ->
272+ if ctor1 /= ctor2 then
273+ Nothing
274+ else
275+ Data ctor1 <$> sequence (zipWith intersection args1 args2)
276+
277+ (Record _, Record _) ->
278+ Just pattern1
279+
280+ (Literal lit1, Literal lit2) ->
281+ if lit1 == lit2 then
282+ Just pattern1
283+ else
284+ Nothing
285+
286+ (AnythingBut literals, Literal lit) ->
287+ if Set.member lit literals then
288+ Nothing
289+ else
290+ Just pattern2
291+
292+ (Literal lit, AnythingBut literals) ->
293+ if Set.member lit literals then
294+ Nothing
295+ else
296+ Just pattern1
297+
298+ (AnythingBut literals1, AnythingBut literals2) ->
299+ Just (AnythingBut (Set.union literals1 literals2))
300+
301+ _ ->
302+ Nothing
303+
304+
305+-- PATTERN COMPLEMENT
306+
307+complement :: TagDict -> Pattern -> [Pattern]
308+complement tagDict nitPattern =
309+ case nitPattern of
310+ Record _fields ->
311+ []
312+
313+ Alias _name pattern ->
314+ complement tagDict pattern
315+
316+ Var _name ->
317+ []
318+
319+ Anything ->
320+ []
321+
322+ Literal lit ->
323+ [AnythingBut (Set.singleton lit)]
324+
325+ AnythingBut literals ->
326+ map Literal (Set.toList literals)
327+
328+ Data ctor patterns ->
329+ complementData tagDict ctor patterns
330+
331+
332+complementData :: TagDict -> Var.Canonical -> [Pattern] -> [Pattern]
333+complementData tagDict tag patterns =
334+ let
335+ otherTags =
336+ lookupOtherTags tag tagDict
337+
338+ tagComplements =
339+ Maybe.mapMaybe (tagToPattern tag) otherTags
340+
341+ arity =
342+ length patterns
343+
344+ argComplements =
345+ concat (zipWith (makeArgComplements tagDict tag arity) [0..] patterns)
346+ in
347+ tagComplements ++ argComplements
348+
349+
350+tagToPattern :: Var.Canonical -> (Tag, Int) -> Maybe Pattern
351+tagToPattern (Var.Canonical home rootTag) (tag, arity) =
352+ if rootTag == tag then
353+ Nothing
354+ else
355+ Just (Data (Var.Canonical home tag) (replicate arity Anything))
356+
357+
358+makeArgComplements :: TagDict -> Var.Canonical -> Int -> Int -> Pattern -> [Pattern]
359+makeArgComplements tagDict tag arity index argPattern =
360+ let
361+ complementList =
362+ complement tagDict argPattern
363+
364+ padArgs pattern =
365+ replicate index Anything
366+ ++ pattern
367+ : replicate (arity - index - 1) Anything
368+ in
369+ map (Data tag . padArgs) complementList
370+
src/Optimize.hsnew file+19
@@ -0,0 +1,19 @@
1+module Optimize where
2+
3+import qualified AST.Module as M
4+import qualified Optimize.TailCalls as TC
5+
6+
7+optimize :: M.CanonicalModule -> M.Optimized
8+optimize modul =
9+ let
10+ canonicalBody =
11+ M.body modul
12+
13+ optimizedProgram =
14+ TC.optimize (M.program canonicalBody)
15+
16+ optimizedBody =
17+ canonicalBody { M.program = optimizedProgram }
18+ in
19+ modul { M.body = optimizedBody }
src/Generate/Cases.hs → src/Optimize/Cases.hsrenamed+14−10
@@ -1,4 +1,4 @@
1−module Generate.Cases (toMatch, Match (..), Clause (..), matchSubst, newVar) where
1+module Optimize.Cases (toMatch, Match (..), Clause (..), matchSubst, newVar) where
22
33import Control.Applicative ((<$>))
44import Control.Arrow (first)
@@ -8,11 +8,11 @@ import qualified Data.List as List
88import Data.Maybe (fromMaybe)
99
1010import qualified AST.Expression.General as Expr
11−import qualified AST.Expression.Canonical as Canonical
11+import qualified AST.Expression.Optimized as Optimized
1212import qualified AST.Literal as Literal
1313import qualified AST.Pattern as P
1414import qualified AST.Variable as Var
15−import qualified Generate.Substitute as S
15+import qualified Optimize.Substitute as S
1616import qualified Reporting.Annotation as A
1717
1818
@@ -22,23 +22,23 @@ data Match
2222 = Match String [Clause] Match
2323 | Break
2424 | Fail
25− | Other Canonical.Expr
25+ | Other Optimized.Expr
2626 | Seq [Match]
27− deriving Show
27+ deriving (Show)
2828
2929
3030data Clause =
3131 Clause (Either Var.Canonical Literal.Literal) [String] Match
32− deriving Show
32+ deriving (Show)
3333
3434
3535type Branch =
36− ([P.CanonicalPattern], Canonical.Expr)
36+ ([P.Optimized], Optimized.Expr)
3737
3838
3939-- PUBLIC FUNCTIONS
4040
41−toMatch :: [(P.CanonicalPattern, Canonical.Expr)] -> State Int (String, Match)
41+toMatch :: [(P.Optimized, Optimized.Expr)] -> State Int (String, Match)
4242toMatch patterns =
4343 do v <- newVar
4444 (,) v <$> buildMatch [v] (map (first (:[])) patterns) Fail
@@ -61,8 +61,12 @@ matchSubst pairs match =
6161 Seq ms ->
6262 Seq (map (matchSubst pairs) ms)
6363
64− Other (A.A a e) ->
65− Other . A.A a $ foldr ($) e $ map (\(x,y) -> S.subst x (Expr.localVar y)) pairs
64+ Other (A.A ann expr) ->
65+ let
66+ substitutions =
67+ map (\(x,y) -> S.subst x (Expr.localVar y)) pairs
68+ in
69+ Other $ A.A ann $ foldr ($) expr substitutions
6670
6771 Match n cs m ->
6872 Match (varSubst n) (map clauseSubst cs) (matchSubst pairs m)
src/Generate/Substitute.hs → src/Optimize/Substitute.hsrenamed+17−14
@@ -1,50 +1,50 @@
11{-# OPTIONS_GHC -Wall #-}
2−module Generate.Substitute (subst) where
2+module Optimize.Substitute (subst) where
33
44import Control.Arrow (second, (***))
55
66import AST.Expression.General (Expr'(..), PortImpl(..))
7−import qualified AST.Expression.Canonical as Canonical
7+import qualified AST.Expression.Optimized as Optimized
88import qualified AST.Pattern as Pattern
99import qualified AST.Variable as V
1010import Elm.Utils ((|>))
1111import qualified Reporting.Annotation as A
1212
1313
14−subst :: String -> Canonical.Expr' -> Canonical.Expr' -> Canonical.Expr'
14+subst :: String -> Optimized.Expr' -> Optimized.Expr' -> Optimized.Expr'
1515subst old new expression =
1616 let f (A.A a e) = A.A a (subst old new e) in
1717 case expression of
18− Range e1 e2 ->
19− Range (f e1) (f e2)
18+ Range lo hi ->
19+ Range (f lo) (f hi)
2020
2121 ExplicitList exprs ->
2222 ExplicitList (map f exprs)
2323
24− Binop op e1 e2 ->
25− Binop op (f e1) (f e2)
24+ Binop op left right ->
25+ Binop op (f left) (f right)
2626
2727 Lambda pattern body ->
2828 if Pattern.member old pattern
2929 then expression
3030 else Lambda pattern (f body)
3131
32− App e1 e2 ->
33− App (f e1) (f e2)
32+ App func arg ->
33+ App (f func) (f arg)
3434
35− MultiIf branches ->
36− MultiIf (map (f *** f) branches)
35+ MultiIf branches finally ->
36+ MultiIf (map (f *** f) branches) (f finally)
3737
3838 Let defs body ->
3939 if anyShadow
4040 then expression
4141 else Let (map substDef defs) (f body)
4242 where
43− substDef (Canonical.Definition p e t) =
44− Canonical.Definition p (f e) t
43+ substDef (Optimized.Definition facts pattern expr) =
44+ Optimized.Definition facts pattern (f expr)
4545
4646 anyShadow =
47− map (\(Canonical.Definition p _ _) -> p) defs
47+ map (\(Optimized.Definition _ p _) -> p) defs
4848 |> any (Pattern.member old)
4949
5050 Var (V.Canonical home x) ->
@@ -101,3 +101,6 @@ subst old new expression =
101101
102102 Task name expr tipe ->
103103 Task name (f expr) tipe
104+
105+ Crash _ ->
106+ expression
src/Optimize/TailCalls.hsnew file+239
@@ -0,0 +1,239 @@
1+module Optimize.TailCalls where
2+
3+import Control.Applicative
4+import Control.Arrow (first)
5+import qualified Control.Monad as M
6+import qualified Data.List as List
7+import qualified Data.Traversable as T
8+
9+import AST.Expression.General as Expr
10+import qualified AST.Expression.Canonical as Can
11+import qualified AST.Expression.Optimized as Opt
12+import qualified AST.Pattern as P
13+import qualified AST.Variable as Var
14+import qualified Reporting.Annotation as A
15+
16+
17+-- OPTIMIZE FOR TAIL CALLS
18+
19+optimize :: Can.Expr -> Opt.Expr
20+optimize expression =
21+ let
22+ (Result _ optExpr) =
23+ findTailCalls Nothing expression
24+ in
25+ optExpr
26+
27+
28+-- TRACKER TO THREAD FINDINGS THROUGH TRAVERSAL
29+
30+data Result a = Result Bool a
31+
32+
33+instance M.Functor Result where
34+ fmap func (Result hasTailCall value) =
35+ Result hasTailCall (func value)
36+
37+
38+instance Applicative Result where
39+ pure value =
40+ Result False value
41+
42+ (<*>) (Result hasTailCall func) (Result hasTailCall' value) =
43+ Result (hasTailCall || hasTailCall') (func value)
44+
45+
46+mapSnd :: M.Functor box => (a -> box b) -> (x, a) -> box (x, b)
47+mapSnd func (x, a) =
48+ (,) x <$> func a
49+
50+
51+-- CONVERT DEFINITIONS
52+
53+detectTailRecursion :: Can.Def -> Opt.Def
54+detectTailRecursion (Can.Definition pattern expression _) =
55+ let
56+ (args, body) =
57+ Expr.collectLambdas expression
58+
59+ context :: Context
60+ context =
61+ case (pattern, args) of
62+ -- detect if it is a function
63+ (A.A _ (P.Var name), _ : _) ->
64+ Just (name, length args)
65+
66+ _ ->
67+ Nothing
68+
69+ (Result isTailCall optExpr) =
70+ findTailCalls context body
71+
72+ lambda x e =
73+ A.A () (Lambda x e)
74+ in
75+ Opt.Definition
76+ (Opt.Facts (if isTailCall then fmap fst context else Nothing))
77+ (removeAnnotations pattern)
78+ (foldr lambda optExpr (map removeAnnotations args))
79+
80+
81+-- CONVERT EXPRESSIONS
82+
83+type Context = Maybe (String, Int)
84+
85+
86+findTailCalls :: Context -> Can.Expr -> Result Opt.Expr
87+findTailCalls context annExpr@(A.A _ expression) =
88+ let
89+ keepLooking =
90+ findTailCalls context
91+ -- this *might* find a tail call, use this when something is a potential
92+ -- tail call
93+
94+ justConvert =
95+ findTailCalls Nothing
96+ -- this will never find tail calls, use this when you are working with
97+ -- expressions that cannot be turned into tail calls
98+ in
99+ A.A () <$>
100+ case expression of
101+ Literal lit ->
102+ pure (Literal lit)
103+
104+ Var name ->
105+ pure (Var name)
106+
107+ Range lowExpr highExpr ->
108+ Range
109+ <$> justConvert lowExpr
110+ <*> justConvert highExpr
111+
112+ ExplicitList elements ->
113+ ExplicitList
114+ <$> T.traverse justConvert elements
115+
116+ Binop op leftExpr rightExpr ->
117+ Binop op
118+ <$> justConvert leftExpr
119+ <*> justConvert rightExpr
120+
121+ Lambda pattern body ->
122+ Lambda (removeAnnotations pattern) <$> justConvert body
123+
124+ App _ _ ->
125+ let
126+ (func:args) =
127+ Expr.collectApps annExpr
128+
129+ isTailCall =
130+ case (context, func) of
131+ ( Just (name, argNum), A.A _ (Var (Var.Canonical Var.Local name')) ) ->
132+ name == name' && length args == argNum
133+ _ ->
134+ False
135+
136+ (Result _ (A.A _ optFunc)) =
137+ justConvert func
138+
139+ (Result _ optArgs) =
140+ T.traverse justConvert args
141+
142+ apply f arg =
143+ App (A.A () f) arg
144+ in
145+ Result isTailCall (List.foldl' apply optFunc optArgs)
146+
147+
148+ MultiIf branches finally ->
149+ let
150+ crawlBranch (cond,branch) =
151+ (,) <$> justConvert cond <*> keepLooking branch
152+ in
153+ MultiIf
154+ <$> T.traverse crawlBranch branches
155+ <*> keepLooking finally
156+
157+ Let defs body ->
158+ let
159+ optDefs = map detectTailRecursion defs
160+ in
161+ Let optDefs <$> keepLooking body
162+
163+ Case expr cases ->
164+ let
165+ preppedCases =
166+ map (first removeAnnotations) cases
167+ in
168+ Case
169+ <$> justConvert expr
170+ <*> T.traverse (mapSnd keepLooking) preppedCases
171+
172+ Data name exprs ->
173+ Data name <$> T.traverse justConvert exprs
174+
175+ Access record field ->
176+ Access
177+ <$> justConvert record
178+ <*> pure field
179+
180+ Remove record field ->
181+ Remove
182+ <$> justConvert record
183+ <*> pure field
184+
185+ Insert record field expr ->
186+ Insert
187+ <$> justConvert record
188+ <*> pure field
189+ <*> justConvert expr
190+
191+ Modify record fields ->
192+ Modify
193+ <$> justConvert record
194+ <*> T.traverse (mapSnd justConvert) fields
195+
196+ Record fields ->
197+ Record
198+ <$> T.traverse (mapSnd justConvert) fields
199+
200+ GLShader uid src gltipe ->
201+ pure (GLShader uid src gltipe)
202+
203+ Port impl ->
204+ Port <$>
205+ case impl of
206+ In name tipe ->
207+ pure (In name tipe)
208+
209+ Out name expr tipe ->
210+ (\e -> Out name e tipe) <$> justConvert expr
211+
212+ Task name expr tipe ->
213+ (\e -> Task name e tipe) <$> justConvert expr
214+
215+ Crash details ->
216+ pure (Crash details)
217+
218+
219+removeAnnotations :: P.CanonicalPattern -> P.Optimized
220+removeAnnotations (A.A _ pattern) =
221+ A.A () $
222+ case pattern of
223+ P.Var x ->
224+ P.Var x
225+
226+ P.Literal lit ->
227+ P.Literal lit
228+
229+ P.Record fields ->
230+ P.Record fields
231+
232+ P.Anything ->
233+ P.Anything
234+
235+ P.Alias alias realPattern ->
236+ P.Alias alias (removeAnnotations realPattern)
237+
238+ P.Data name patterns ->
239+ P.Data name (map removeAnnotations patterns)
src/Parse/Expression.hs+27−18
@@ -18,6 +18,8 @@ import qualified AST.Literal as L
1818import qualified AST.Pattern as P
1919import qualified AST.Variable as Var
2020import qualified Reporting.Annotation as A
21+import qualified Reporting.Crash as Crash
22+import qualified Reporting.Region as R
2123
2224
2325-------- Basic Terms --------
@@ -220,7 +222,8 @@ appExpr =
220222
221223expr :: IParser Source.Expr
222224expr =
223− addLocation (choice [ ifExpr, letExpr, caseExpr ])
225+ addLocation (choice [ letExpr, caseExpr ])
226+ <|> ifExpr
224227 <|> lambdaExpr
225228 <|> binaryExpr
226229 <?> "an expression"
@@ -231,39 +234,45 @@ binaryExpr =
231234 Binop.binops appExpr lastExpr anyOp
232235 where
233236 lastExpr =
234− addLocation (choice [ ifExpr, letExpr, caseExpr ])
237+ addLocation (choice [ letExpr, caseExpr ])
238+ <|> ifExpr
235239 <|> lambdaExpr
236240 <?> "an expression"
237241
238242
239−ifExpr :: IParser Source.Expr'
243+ifExpr :: IParser Source.Expr
240244ifExpr =
241− do try (reserved "if")
245+ do start <- getMyPosition
246+ try (reserved "if")
242247 whitespace
243− normal <|> multiIf
248+ makeExpr <- normal <|> multiIf
249+ end <- getMyPosition
250+ return (makeExpr (R.Region start end))
244251 where
245252 normal =
246− do bool <- expr
253+ do condition <- expr
247254 padded (reserved "then")
248255 thenBranch <- expr
249256 whitespace <?> "an 'else' branch"
250257 reserved "else" <?> "an 'else' branch"
251258 whitespace
252259 elseBranch <- expr
253− return $ E.MultiIf
254− [ (bool, thenBranch)
255− , (A.sameAs elseBranch (E.Literal . L.Boolean $ True), elseBranch)
256− ]
260+ return $ \region -> A.A region (E.MultiIf [(condition, thenBranch)] elseBranch)
257261
258262 multiIf =
259− E.MultiIf <$> spaceSep1 iff
260− where
261− iff =
262− do string "|"
263− whitespace
264− b <- expr
265− padded rightArrow
266− (,) b <$> expr
263+ do branches <- spaceSep1 multiBranch
264+ return $ \region -> A.A region (E.MultiIf branches (crash region))
265+
266+ crash region =
267+ A.A region (E.Crash (Crash.IncompleteMultiIf region))
268+
269+ multiBranch =
270+ do string "|"
271+ whitespace
272+ condition <- expr
273+ padded rightArrow
274+ branch <- expr
275+ return (condition, branch)
267276
268277
269278lambdaExpr :: IParser Source.Expr
src/Reporting/Crash.hsnew file+20
@@ -0,0 +1,20 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Reporting.Crash where
3+
4+import qualified Reporting.Region as R
5+
6+
7+data Details
8+ = IncompleteMultiIf R.Region
9+ | IncompletePatternMatch R.Region
10+ deriving (Show)
11+
12+
13+summary :: Details -> String
14+summary details =
15+ case details of
16+ IncompleteMultiIf _ ->
17+ "incomplete-multi-if"
18+
19+ IncompletePatternMatch _ ->
20+ "incomplete-pattern-match"
src/Reporting/Error/Type.hs+7
@@ -37,6 +37,7 @@ data InfiniteType = InfiniteTypeInfo
3737data Hint
3838 = CaseBranch Int Region.Region
3939 | Case
40+ | IfCondition
4041 | IfBranches
4142 | MultiIfBranch Int Region.Region
4243 | If
@@ -128,6 +129,12 @@ hintToString hint =
128129 , Nothing
129130 )
130131
132+ IfCondition ->
133+ ( Nothing
134+ , "This condition does not evaluate to a boolean value, True or False."
135+ , Just "Elm does not have \"truthiness\" such that ints and strings and lists are\nautomatically converted to booleans. Do that conversion explicitly."
136+ )
137+
131138 IfBranches ->
132139 ( Nothing
133140 , "The branches of this `if` result in different types of values."
src/Reporting/Warning.hs+42
@@ -9,6 +9,7 @@ import Text.PrettyPrint ((<+>))
99
1010import qualified AST.Module as Module
1111import qualified AST.Type as Type
12+import qualified Nitpick.Pattern as Pattern
1213import qualified Reporting.Annotation as A
1314import qualified Reporting.PrettyPrint as P
1415import qualified Reporting.Report as Report
@@ -19,6 +20,8 @@ import qualified Reporting.Report as Report
1920data Warning
2021 = UnusedImport Module.Name
2122 | MissingTypeAnnotation String Type.Canonical
23+ | InexhaustivePatternMatch [Pattern.Pattern]
24+ | RedundantPatternMatch
2225
2326
2427-- TO STRING
@@ -56,6 +59,32 @@ toReport dealiaser warning =
5659 4
5760 (P.pretty dealiaser False inferredType)
5861
62+ InexhaustivePatternMatch unhandled ->
63+ Report.simple
64+ "missing pattern"
65+ "The following case expression does not handle all possible inputs."
66+ ( "The following patterns are not being handled:\n"
67+ ++ viewPatternList unhandled
68+ ++ "\n\n"
69+ ++ "If we get values like this, we have no choice but to crash! Normally this means\n"
70+ ++ "you just added a new tag to a union type, but sometimes it can be because there\n"
71+ ++ "is something very odd about the data you are modeling. In those rare cases, you\n"
72+ ++ "probably want to either:\n"
73+ ++ "\n"
74+ ++ " 1. Rethink the model. Maybe some cases are \"impossible\" but it is still\n"
75+ ++ " possible to construct such cases. Is there a way to model the values more\n"
76+ ++ " precisely such that \"impossible\" values truly are impossible?\n"
77+ ++ " 2. End the pattern match with a wildcard match that leads to an expression\n"
78+ ++ " like: Debug.crash \"If you are reading this, I have made a mistake!\"\n"
79+ ++ " Generally speaking, you do not want it to have to be this way."
80+ )
81+
82+ RedundantPatternMatch ->
83+ Report.simple
84+ "redundant pattern"
85+ "The following pattern is redundant."
86+ "Any value with this shape will be handled by a previous pattern."
87+
5988
6089-- TO JSON
6190
@@ -71,3 +100,16 @@ toJson dealiaser filePath (A.A region warning) =
71100 , "type" .= ("warning" :: String)
72101 ]
73102 ++ additionalFields
103+
104+
105+-- PATTERN WARNINGS
106+
107+viewPatternList :: [Pattern.Pattern] -> String
108+viewPatternList unhandledPatterns =
109+ let
110+ (showPatterns, rest) =
111+ splitAt 4 unhandledPatterns
112+ in
113+ concatMap ((++) "\n ") $
114+ map Pattern.toString showPatterns
115+ ++ if null rest then [] else ["..."]
src/Type/Constrain/Expression.hs+28−11
@@ -2,6 +2,7 @@
22module Type.Constrain.Expression where
33
44import Control.Applicative ((<$>))
5+import Control.Arrow (second)
56import qualified Control.Monad as Monad
67import qualified Data.List as List
78import qualified Data.Map as Map
@@ -97,8 +98,8 @@ constrain env annotatedExpr@(A.A region expression) tipe =
9798 in
9899 constrainApp env region f args tipe
99100
100− E.MultiIf branches ->
101− constrainIf env region branches tipe
101+ E.MultiIf branches finally ->
102+ constrainIf env region branches finally tipe
102103
103104 E.Case expr branches ->
104105 constrainCase env region expr branches tipe
@@ -184,6 +185,9 @@ constrain env annotatedExpr@(A.A region expression) tipe =
184185 E.Task _ expr _ ->
185186 constrain env expr tipe
186187
188+ E.Crash _ ->
189+ return CTrue
190+
187191
188192-- CONSTRAIN APP
189193
@@ -333,25 +337,38 @@ constrainIf
333337 :: Env.Environment
334338 -> R.Region
335339 -> [(Canonical.Expr, Canonical.Expr)]
340+ -> Canonical.Expr
336341 -> Type
337342 -> IO TypeConstraint
338−constrainIf env region branches tipe =
339− do (branchInfo, branchExprCons) <-
340− unzip <$> mapM constrainBranch branches
343+constrainIf env region branches finally tipe =
344+ do let (conditions, branchExprs) =
345+ second (++ [finally]) (unzip branches)
346+
347+ (condVars, condCons) <-
348+ unzip <$> mapM constrainCondition conditions
349+
350+ (branchInfo, branchExprCons) <-
351+ unzip <$> mapM constrainBranch branchExprs
341352
342353 (vars,cons) <- branchCons branchInfo
343354
344− return $ ex vars (CAnd (branchExprCons ++ cons))
355+ return $ ex (condVars ++ vars) (CAnd (condCons ++ branchExprCons ++ cons))
345356 where
346− bool = Env.get env Env.types "Bool"
357+ bool =
358+ Env.get env Env.types "Bool"
359+
360+ constrainCondition condition@(A.A condRegion _) =
361+ do condVar <- variable Flexible
362+ condCon <- constrain env condition (varN condVar)
363+ let boolCon = CEqual Error.IfCondition condRegion (varN condVar) bool
364+ return (condVar, CAnd [ condCon, boolCon ])
347365
348− constrainBranch (cond, expr@(A.A branchRegion _)) =
366+ constrainBranch expr@(A.A branchRegion _) =
349367 do branchVar <- variable Flexible
350− condCon <- constrain env cond bool
351368 exprCon <- constrain env expr (varN branchVar)
352369 return
353370 ( (branchVar, branchRegion)
354− , condCon /\ exprCon
371+ , exprCon
355372 )
356373
357374 branchCons branchInfo =
@@ -368,7 +385,7 @@ constrainIf env region branches tipe =
368385 pairCons region Error.MultiIfBranch varToCon branchInfo
369386
370387 varToCon var =
371− CEqual Error.If region tipe (varN var)
388+ CEqual Error.If region (varN var) tipe
372389
373390
374391-- CONSTRAIN CASE EXPRESSIONS
src/Validate.hs+7−2
@@ -213,8 +213,10 @@ expression (A.A ann sourceExpression) =
213213 <$> expression funcExpr
214214 <*> expression argExpr
215215
216− MultiIf branches ->
217− MultiIf <$> T.traverse both branches
216+ MultiIf branches finally ->
217+ MultiIf
218+ <$> T.traverse both branches
219+ <*> expression finally
218220
219221 Access record field ->
220222 Access
@@ -263,6 +265,9 @@ expression (A.A ann sourceExpression) =
263265 do expr' <- expression expr
264266 return (Task name expr' tipe)
265267
268+ Crash details ->
269+ return (Crash details)
270+
266271
267272second :: (a, Source.Expr) -> Result.Result wrn Error.Error (a, Valid.Expr)
268273second (value, expr) =