on master help sign in
Merge branch 'tce' into pattern-warnings

Conflicts: src/Nitpick/Pattern.hs src/Nitpick/PatternMatches.hs

commit67f928204aaadf6a433ca9dde5921567dbc8b9d3 authorEvan Czaplicki <info@elm-lang.org> dateFri 14 Aug 2015, 17:17 (11 years ago) parentd9cf19a5c78998
80 files changed+4006−1847
.travis.yml+17−13 README.md+1−1 Setup.hs+2 elm-compiler.cabal+22−77 src/AST/Declaration.hs+5−4 src/AST/Expression/Canonical.hs+9−4 src/AST/Expression/General.hs+56−38 src/AST/Expression/Optimized.hs+35 src/AST/Literal.hs+1−1 src/AST/Module.hs+27−9 src/AST/Pattern.hs+11−7 src/AST/Type.hs+178−86 src/AST/Variable.hs+11−7 src/Canonicalize.hs+150−92 src/Canonicalize/Declaration.hs+10−9 src/Canonicalize/Environment.hs+34 src/Canonicalize/Port.hs+18−14 src/Canonicalize/Setup.hs+3−2 src/Canonicalize/Sort.hs+17−5 src/Canonicalize/Variable.hs+3−2 src/CommandLineRouter.hs+2−2 src/Compile.hs+9−3 src/Docs.hs−275 src/Docs/AST.hs+44 src/Docs/Centralize.hs+100 src/Docs/Check.hs+180 src/Elm/Compiler.hs+126−37 src/Elm/Compiler/Module.hs+4−4 src/Elm/Compiler/Type.hs+62−49 src/Elm/Compiler/Type/Extract.hs+23−9 src/Elm/Docs.hs+82−3 src/Elm/Utils.hs+30−3 src/Generate/JavaScript.hs+602−268 src/Generate/JavaScript/Crash.hs+32 src/Generate/JavaScript/Helpers.hs−19 src/Generate/JavaScript/Port.hs+4−3 src/Nitpick/TopLevelTypes.hs+115 src/Optimize.hs+19 src/Generate/Cases.hs → src/Optimize/Cases.hs+16−12 src/Generate/Substitute.hs → src/Optimize/Substitute.hs+17−14 src/Optimize/TailCalls.hs+239 src/Parse/Declaration.hs+11−4 src/Parse/Expression.hs+84−50 src/Parse/Helpers.hs+85−50 src/Parse/Literal.hs+4−4 src/Parse/Module.hs+20−11 src/Parse/Parse.hs+11−10 src/Parse/Pattern.hs+2−2 src/Parse/Type.hs+3−3 src/Reporting/Annotation.hs+6−2 src/Reporting/Crash.hs+20 src/Reporting/Error.hs+60−13 src/Reporting/Error/Canonicalize.hs+68−67 src/Reporting/Error/Docs.hs+61 src/Reporting/Error/Helpers.hs+39 src/Reporting/Error/Nitpick.hs−25 src/Reporting/Error/Syntax.hs+130−48 src/Reporting/Error/Type.hs+241−54 src/Reporting/PrettyPrint.hs+14−2 src/Reporting/Report.hs+180−52 src/Reporting/Result.hs+41−15 src/Reporting/Warning.hs+56−10 src/Reporting/Warning/Nitpick.hs−14 src/Type/Constrain/Expression.hs+270−85 src/Type/Constrain/Literal.hs+9−1 src/Type/Constrain/Pattern.hs+34−13 src/Type/Environment.hs+3−3 src/Type/ExtraChecks.hs−78 src/Type/Fragment.hs+2−1 src/Type/Inference.hs+7−15 src/Type/Solve.hs+55−39 src/Type/State.hs+1−1 src/Type/Type.hs+21−13 src/Type/Unify.hs+50−30 src/Validate.hs+60−33 tests/Test.hs+3−3 tests/Test/Compiler.hs+25−10 tests/Test/Property.hs+1−1 tests/test-files/good/Ports.elm+11−11 tests/test-files/good/PortsWithTypeAliases.elm+2−2
.travis.yml+17−13
@@ -4,28 +4,32 @@
44
55# The following enables several GHC versions to be tested; often it's enough to test only against the last release in a major GHC version. Feel free to omit lines listings versions you don't need/want testing for.
66env:
7− - GHCVER=7.6.3
8− - GHCVER=7.8.2
7+ - CABALVER=1.18 GHCVER=7.6.3
8+ - CABALVER=1.18 GHCVER=7.8.3
9+ - CABALVER=1.22 GHCVER=7.10.1
10+
911
1012# Note: the distinction between `before_install` and `install` is not important.
1113before_install:
1214 - travis_retry sudo add-apt-repository -y ppa:hvr/ghc
1315 - travis_retry sudo apt-get update
14− - travis_retry sudo apt-get install cabal-install-1.18 ghc-$GHCVER happy alex # see note about happy/alex
15− - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/1.18/bin:$PATH
16− - cabal update
17− # Update happy when building with GHC 7.8 or head
16+ - travis_retry sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER # see note about happy/alex
17+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
1818 - |
19− if [ $GHCVER = "head" ] || [ $GHCVER = "7.8.2" ]; then
20− cabal install happy alex
21− export PATH=$HOME/.cabal/bin:$PATH
22− fi
19+ if [ ${GHCVER%.*} = "7.8" ] || [ ${GHCVER%.*} = "7.10" ]; then
20+ travis_retry sudo apt-get install happy-1.19.4 alex-3.1.3
21+ export PATH=/opt/alex/3.1.3/bin:/opt/happy/1.19.4/bin:$PATH
22+ else
23+ travis_retry sudo apt-get install happy alex
24+ fi
25+
2326
2427install:
25− - cabal install --only-dependencies --enable-tests --enable-benchmarks
28+ - travis_retry cabal update
29+ - cabal install --only-dependencies --enable-tests
30+
2631
27−# Here starts the actual work to be performed for the package under test; any command which exits with a non-zero exit code causes the build to fail.
2832script:
2933 - cabal configure --enable-tests
3034 - cabal build
31− - cabal test
35+ - cabal test
README.md+1−1
@@ -1,6 +1,6 @@
11Learn about the Elm programming language at [elm-lang.org](http://elm-lang.org/).
22
3−[![Build Status](https://travis-ci.org/elm-lang/elm-compiler.png)](https://travis-ci.org/elm-lang/elm-compiler)
3+[![Build Status](https://travis-ci.org/elm-lang/elm-compiler.svg)](https://travis-ci.org/elm-lang/elm-compiler)
44
55## Install
66
Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
elm-compiler.cabal+22−77
@@ -1,6 +1,6 @@
11
22Name: elm-compiler
3−Version: 0.15
3+Version: 0.15.1
44
55Synopsis:
66 Values to help with elm-package, elm-make, and elm-lang.org.
@@ -33,7 +33,7 @@ source-repository head
3333
3434Library
3535 ghc-options:
36− -threaded -O2 -W -Werror
36+ -O2 -W
3737
3838 Hs-Source-Dirs:
3939 src
@@ -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,
@@ -67,15 +68,22 @@ Library
6768 Canonicalize.Type,
6869 Canonicalize.Variable,
6970 Compile,
71+ Docs.AST,
72+ Docs.Centralize,
73+ Docs.Check,
7074 Elm.Compiler.Imports,
7175 Elm.Compiler.Type.Extract,
7276 Elm.Compiler.Version,
73− Generate.Cases,
7477 Generate.JavaScript,
78+ Generate.JavaScript.Crash,
7579 Generate.JavaScript.Helpers,
7680 Generate.JavaScript.Port,
7781 Generate.JavaScript.Variable,
78− Generate.Substitute,
82+ Nitpick.TopLevelTypes,
83+ Optimize,
84+ Optimize.Cases,
85+ Optimize.Substitute,
86+ Optimize.TailCalls,
7987 Parse.Binop,
8088 Parse.Declaration,
8189 Parse.Expression,
@@ -86,8 +94,11 @@ Library
8694 Parse.Pattern,
8795 Parse.Type,
8896 Reporting.Annotation,
97+ Reporting.Crash,
8998 Reporting.Error,
9099 Reporting.Error.Canonicalize,
100+ Reporting.Error.Docs,
101+ Reporting.Error.Helpers,
91102 Reporting.Error.Syntax,
92103 Reporting.Error.Type,
93104 Reporting.PrettyPrint,
@@ -99,7 +110,6 @@ Library
99110 Type.Constrain.Literal,
100111 Type.Constrain.Pattern,
101112 Type.Environment,
102− Type.ExtraChecks,
103113 Type.Fragment,
104114 Type.Inference,
105115 Type.PrettyPrint,
@@ -113,6 +123,7 @@ Library
113123 Build-depends:
114124 aeson >= 0.7 && < 0.9,
115125 aeson-pretty >= 0.7 && < 0.8,
126+ ansi-terminal >= 0.6.2.1 && < 0.7,
116127 base >=4.2 && <5,
117128 binary >= 0.7.0.0 && < 0.8,
118129 blaze-html >= 0.5 && < 0.8,
@@ -125,7 +136,7 @@ Library
125136 filepath >= 1 && < 2.0,
126137 indents >= 0.3 && < 0.4,
127138 language-ecmascript >= 0.15 && < 0.18,
128− language-glsl >= 0.0.2 && < 0.2,
139+ language-glsl >= 0.0.2 && < 0.3,
129140 mtl >= 2.2 && < 3,
130141 parsec >= 3.1.1 && < 3.5,
131142 pretty >= 1.0 && < 2.0,
@@ -136,56 +147,6 @@ Library
136147 unordered-containers >= 0.1 && < 0.3
137148
138149
139−Executable elm-doc
140− ghc-options:
141− -threaded -O2 -W
142−
143− Hs-Source-Dirs:
144− src
145−
146− Main-is:
147− Docs.hs
148−
149− other-modules:
150− Elm.Compiler.Version,
151− AST.Declaration,
152− AST.Expression.General,
153− AST.Helpers,
154− AST.Literal,
155− AST.Module,
156− AST.Pattern,
157− AST.Type,
158− AST.Variable,
159− Parse.Binop,
160− Parse.Declaration,
161− Parse.Expression,
162− Parse.Helpers,
163− Parse.Literal,
164− Parse.Module,
165− Parse.Pattern,
166− Parse.Type
167−
168− Build-depends:
169− aeson >= 0.7 && < 0.9,
170− aeson-pretty >= 0.7 && < 0.8,
171− base >=4.2 && <5,
172− binary >= 0.7.0.0 && < 0.8,
173− bytestring >= 0.9 && < 0.11,
174− cmdargs >= 0.7 && < 0.11,
175− containers >= 0.3 && < 0.6,
176− directory >= 1.0 && < 2.0,
177− filepath >= 1 && < 2.0,
178− indents >= 0.3 && < 0.4,
179− language-glsl >= 0.0.2 && < 0.2,
180− mtl >= 2.2 && < 3,
181− parsec >= 3.1.1 && < 3.5,
182− pretty >= 1.0 && < 2.0,
183− process,
184− text >= 1 && < 2,
185− transformers >= 0.2 && < 0.5,
186− union-find >= 0.2 && < 0.3
187−
188−
189150Executable elm
190151 ghc-options:
191152 -threaded -O2 -W
@@ -200,24 +161,9 @@ Executable elm
200161 Elm.Compiler.Version
201162
202163 Build-depends:
203− aeson >= 0.7 && < 0.9,
204− aeson-pretty >= 0.7 && < 0.8,
205164 base >=4.2 && <5,
206− binary >= 0.7.0.0 && < 0.8,
207− bytestring >= 0.9 && < 0.11,
208− cmdargs >= 0.7 && < 0.11,
209− containers >= 0.3 && < 0.6,
210165 directory >= 1.0 && < 2.0,
211− filepath >= 1 && < 2.0,
212− indents >= 0.3 && < 0.4,
213− language-glsl >= 0.0.2 && < 0.2,
214− mtl >= 2.2 && < 3,
215− parsec >= 3.1.1 && < 3.5,
216− pretty >= 1.0 && < 2.0,
217− process,
218− text >= 1 && < 2,
219− transformers >= 0.2 && < 0.5,
220− union-find >= 0.2 && < 0.3
166+ process
221167
222168
223169Test-Suite compiler-tests
@@ -236,7 +182,7 @@ Test-Suite compiler-tests
236182 Test.Property.Arbitrary
237183 AST.Helpers
238184 AST.Literal
239− AST.PrettyPrint
185+ Reporting.PrettyPrint
240186
241187 build-depends:
242188 test-framework > 0.8 && < 0.9,
@@ -246,6 +192,7 @@ Test-Suite compiler-tests
246192 QuickCheck >= 2 && < 3,
247193 aeson >= 0.7 && < 0.9,
248194 aeson-pretty >= 0.7 && < 0.8,
195+ ansi-terminal >= 0.6.2.1 && < 0.7,
249196 base >=4.2 && <5,
250197 binary >= 0.7.0.0 && < 0.8,
251198 blaze-html >= 0.5 && < 0.8,
@@ -260,13 +207,11 @@ Test-Suite compiler-tests
260207 filepath >= 1 && < 2.0,
261208 indents >= 0.3 && < 0.4,
262209 language-ecmascript >= 0.15 && < 0.18,
263− language-glsl >= 0.0.2 && < 0.2,
210+ language-glsl >= 0.0.2 && < 0.3,
264211 mtl >= 2.2 && < 3,
265212 parsec >= 3.1.1 && < 3.5,
266213 pretty >= 1.0 && < 2.0,
267214 process,
268215 text >= 1 && < 2,
269216 transformers >= 0.2 && < 0.5,
270− union-find >= 0.2 && < 0.3,
271− unordered-containers >= 0.1 && < 0.3
272−
217+ union-find >= 0.2 && < 0.3
src/AST/Declaration.hs+5−4
@@ -41,16 +41,17 @@ type SourceDecl' =
4141 Declaration' SourcePort Source.Def Type.Raw Source.Expr
4242
4343
44−type SourceDecl =
45− A.Located SourceDecl'
44+data SourceDecl
45+ = Comment String
46+ | Decl (A.Located SourceDecl')
4647
4748
4849type ValidDecl =
49− A.Located (Declaration' ValidPort Valid.Def Type.Raw Valid.Expr)
50+ A.Commented (Declaration' ValidPort Valid.Def Type.Raw Valid.Expr)
5051
5152
5253type CanonicalDecl =
53− A.Located (Declaration' CanonicalPort Canonical.Def Type.Canonical Canonical.Expr)
54+ A.Commented (Declaration' CanonicalPort Canonical.Def Type.Canonical Canonical.Expr)
5455
5556
5657-- PORTS
src/AST/Expression/Canonical.hs+9−4
@@ -7,6 +7,7 @@ import qualified AST.Expression.General as General
77import qualified AST.Pattern as Pattern
88import qualified AST.Type as Type
99import qualified AST.Variable as Var
10+import qualified Reporting.Annotation as A
1011import qualified Reporting.PrettyPrint as P
1112import qualified Reporting.Region as R
1213
@@ -23,16 +24,18 @@ type Expr' =
2324
2425
2526data Def
26− = Definition Pattern.CanonicalPattern Expr (Maybe Type.Canonical)
27+ = Definition Pattern.CanonicalPattern Expr (Maybe (A.Located Type.Canonical))
2728 deriving (Show)
2829
2930
3031instance P.Pretty Def where
31− pretty _ (Definition pattern expr maybeTipe) =
32+ pretty dealiaser _ (Definition pattern expr maybeTipe) =
3233 P.vcat [ annotation, definition ]
3334 where
3435 definition =
35− P.pretty True pattern <+> P.equals <+> P.pretty False expr
36+ P.pretty dealiaser True pattern
37+ <+> P.equals
38+ <+> P.pretty dealiaser False expr
3639
3740 annotation =
3841 case maybeTipe of
@@ -40,4 +43,6 @@ instance P.Pretty Def where
4043 P.empty
4144
4245 Just tipe ->
43− P.pretty True pattern <+> P.colon <+> P.pretty False tipe
46+ P.pretty dealiaser True pattern
47+ <+> P.colon
48+ <+> P.pretty dealiaser False tipe
src/AST/Expression/General.hs+56−38
@@ -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
@@ -113,64 +115,75 @@ dummyLet defs =
113115-- PRETTY PRINTING
114116
115117instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann def var typ) where
116− pretty needsParens expression =
118+ pretty dealiaser needsParens expression =
117119 case expression of
118120 Literal literal ->
119− P.pretty needsParens literal
121+ P.pretty dealiaser needsParens literal
120122
121123 Var x ->
122− P.pretty needsParens x
124+ P.pretty dealiaser needsParens x
123125
124126 Range lowExpr highExpr ->
125127 P.brackets $
126− P.pretty False lowExpr
128+ P.pretty dealiaser False lowExpr
127129 <> P.text ".."
128− <> P.pretty False highExpr
130+ <> P.pretty dealiaser False highExpr
129131
130132 ExplicitList elements ->
131− P.brackets (P.commaCat (map (P.pretty False) elements))
133+ P.brackets (P.commaCat (map (P.pretty dealiaser False) elements))
132134
133135 Binop op (A.A _ (Literal (Literal.IntNum 0))) expr
134136 | Var.toString op == "-" ->
135− P.text "-" <> P.pretty True expr
137+ P.text "-" <> P.pretty dealiaser True expr
136138
137139 Binop op leftExpr rightExpr ->
138140 P.parensIf needsParens $
139141 P.hang
140− (P.pretty True leftExpr)
142+ (P.pretty dealiaser True leftExpr)
141143 2
142− (P.text op'' <+> P.pretty True rightExpr)
144+ (P.text op'' <+> P.pretty dealiaser True rightExpr)
143145 where
144146 op' = Var.toString op
145147 op'' = if Help.isOp op' then op' else "`" ++ op' ++ "`"
146148
147149 Lambda pattern expr ->
148150 P.parensIf needsParens $
149− P.text "\\" <> args <+> P.text "->" <+> P.pretty False body
151+ P.text "\\" <> args <+> P.text "->" <+> P.pretty dealiaser False body
150152 where
151153 (patterns, body) =
152154 collectLambdas expr
153155
154156 args =
155− P.sep (map (P.pretty True) (pattern : patterns))
157+ P.sep (map (P.pretty dealiaser True) (pattern : patterns))
156158
157159 App expr arg ->
158160 P.parensIf needsParens $
159161 P.hang func 2 (P.sep args)
160162 where
161163 func:args =
162− map (P.pretty True) (collectApps expr ++ [arg])
164+ 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 "|" <+>
170180 P.hang
171− (P.pretty False condition <+> P.text "->")
181+ (P.pretty dealiaser False condition <+> P.text "->")
172182 2
173− (P.pretty False branch)
183+ (P.pretty dealiaser False branch)
184+
185+ final =
186+ P.hang (P.text "| True -> ") 2 (P.pretty dealiaser False finally)
174187
175188 Let defs body ->
176189 P.parensIf needsParens $
@@ -178,8 +191,8 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
178191 [ P.hang
179192 (P.text "let")
180193 4
181− (P.vcat (map (P.pretty False) defs))
182− , P.text "in" <+> P.pretty False body
194+ (P.vcat (map (P.pretty dealiaser False) defs))
195+ , P.text "in" <+> P.pretty dealiaser False body
183196 ]
184197
185198 Case expr branches ->
@@ -187,16 +200,16 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
187200 P.hang pexpr 2 (P.vcat (map pretty' branches))
188201 where
189202 pexpr =
190− P.text "case" <+> P.pretty False expr <+> P.text "of"
203+ P.text "case" <+> P.pretty dealiaser False expr <+> P.text "of"
191204
192205 pretty' (pattern, branch) =
193− P.pretty False pattern
206+ P.pretty dealiaser False pattern
194207 <+> P.text "->"
195− <+> P.pretty False branch
208+ <+> P.pretty dealiaser False branch
196209
197210 Data "::" [hd,tl] ->
198211 P.parensIf needsParens $
199− P.pretty True hd <+> P.text "::" <+> P.pretty True tl
212+ P.pretty dealiaser True hd <+> P.text "::" <+> P.pretty dealiaser True tl
200213
201214 Data "[]" [] ->
202215 P.text "[]"
@@ -204,49 +217,49 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
204217 Data name exprs ->
205218 if Help.isTuple name
206219 then
207− P.parens (P.commaCat (map (P.pretty False) exprs))
220+ P.parens (P.commaCat (map (P.pretty dealiaser False) exprs))
208221 else
209222 P.parensIf (needsParens && not (null exprs)) $
210223 P.hang
211224 (P.text name)
212225 2
213− (P.sep (map (P.pretty True) exprs))
226+ (P.sep (map (P.pretty dealiaser True) exprs))
214227
215228 Access record field ->
216− P.pretty True record <> P.text "." <> P.text field
229+ P.pretty dealiaser True record <> P.text "." <> P.text field
217230
218231 Remove record field ->
219− P.braces (P.pretty False record <+> P.text "-" <+> P.text field)
232+ P.braces (P.pretty dealiaser False record <+> P.text "-" <+> P.text field)
220233
221234 Insert (A.A _ (Remove record oldField)) newField expr ->
222235 P.braces $
223236 P.hsep
224− [ P.pretty False record
237+ [ P.pretty dealiaser False record
225238 , P.text "-"
226239 , P.text oldField
227240 , P.text "|"
228241 , P.text newField
229242 , P.equals
230− , P.pretty False expr
243+ , P.pretty dealiaser False expr
231244 ]
232245
233246 Insert record field expr ->
234247 P.braces $
235− P.pretty False record
248+ P.pretty dealiaser False record
236249 <+> P.text "|"
237250 <+> P.text field
238251 <+> P.equals
239− <+> P.pretty False expr
252+ <+> P.pretty dealiaser False expr
240253
241254 Modify record fields ->
242255 P.braces $
243256 P.hang
244− (P.pretty False record <+> P.text "|")
257+ (P.pretty dealiaser False record <+> P.text "|")
245258 4
246259 (P.commaSep $ map prettyField fields)
247260 where
248261 prettyField (field, expr) =
249− P.text field <+> P.text "<-" <+> P.pretty False expr
262+ P.text field <+> P.text "<-" <+> P.pretty dealiaser False expr
250263
251264 Record fields ->
252265 P.sep
@@ -255,17 +268,20 @@ instance (P.Pretty def, P.Pretty var, Var.ToString var) => P.Pretty (Expr' ann d
255268 ]
256269 where
257270 field (name, expr) =
258− P.text name <+> P.equals <+> P.pretty False expr
271+ P.text name <+> P.equals <+> P.pretty dealiaser False expr
259272
260273 GLShader _ _ _ ->
261274 P.text "[glsl| ... |]"
262275
263276 Port portImpl ->
264− P.pretty needsParens portImpl
277+ P.pretty dealiaser needsParens portImpl
278+
279+ Crash details ->
280+ P.text ("<crash:" ++ Crash.summary details ++ ">")
265281
266282
267283instance P.Pretty (PortImpl expr tipe) where
268− pretty _ impl =
284+ pretty _ _ impl =
269285 P.text ("<port:" ++ portName impl ++ ">")
270286
271287
@@ -282,7 +298,9 @@ collectLambdas
282298collectLambdas lexpr@(A.A _ expr) =
283299 case expr of
284300 Lambda pattern body ->
285− let (ps, body') = collectLambdas body
286− in (pattern : ps, body')
301+ let
302+ (ps, body') = collectLambdas body
303+ in
304+ (pattern : ps, body')
287305
288306 _ -> ([], lexpr)
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+1−1
@@ -17,7 +17,7 @@ data Literal
1717
1818
1919instance P.Pretty Literal where
20− pretty _ literal =
20+ pretty _ _ literal =
2121 case literal of
2222 IntNum n -> P.int n
2323 FloatNum n -> P.double n
src/AST/Module.hs+27−9
@@ -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,8 +17,10 @@ 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
23+import qualified Docs.AST as Docs
2224import qualified Elm.Compiler.Version as Compiler
2325import qualified Reporting.Annotation as A
2426
@@ -38,28 +40,41 @@ type CanonicalAdt = (Var.Canonical, AdtInfo Var.Canonical)
3840-- MODULES
3941
4042type SourceModule =
41− Module [UserImport] (Var.Listing Var.Value) [Decl.SourceDecl]
43+ Module
44+ String
45+ [UserImport]
46+ (Var.Listing (A.Located Var.Value))
47+ [Decl.SourceDecl]
48+
4249
4350type ValidModule =
4451 Module
52+ String
4553 ([DefaultImport], [UserImport])
46− (Var.Listing Var.Value)
54+ (Var.Listing (A.Located Var.Value))
4755 [Decl.ValidDecl]
4856
57+
4958type CanonicalModule =
50− Module [Name] [Var.Value] CanonicalBody
59+ Module Docs.Centralized [Name] [Var.Value] (Body Canonical.Expr)
60+
5161
62+type Optimized =
63+ Module Docs.Centralized [Name] [Var.Value] (Body Optimized.Expr)
5264
53−data Module imports exports body = Module
65+
66+data Module docs imports exports body = Module
5467 { names :: Name
5568 , path :: FilePath
69+ , docs :: A.Located (Maybe docs)
5670 , exports :: exports
5771 , imports :: imports
5872 , body :: body
5973 }
6074
61−data CanonicalBody = CanonicalBody
62− { program :: Canonical.Expr
75+
76+data Body expr = Body
77+ { program :: expr
6378 , types :: Types
6479 , fixities :: [(Decl.Assoc, Int, String)]
6580 , aliases :: Aliases
@@ -73,7 +88,8 @@ data CanonicalBody = CanonicalBody
7388{-| Basic info needed to identify modules and determine dependencies. -}
7489data Header imports = Header
7590 { _names :: Name
76− , _exports :: Var.Listing Var.Value
91+ , _docs :: A.Located (Maybe String)
92+ , _exports :: Var.Listing (A.Located Var.Value)
7793 , _imports :: imports
7894 }
7995
@@ -121,6 +137,7 @@ data Interface = Interface
121137 , iPorts :: [String]
122138 }
123139
140+
124141toInterface :: CanonicalModule -> Interface
125142toInterface modul =
126143 let body' = body modul in
@@ -135,6 +152,7 @@ toInterface modul =
135152 , iPorts = ports body'
136153 }
137154
155+
138156instance Binary Interface where
139157 get = Interface <$> get <*> get <*> get <*> get <*> get <*> get <*> get <*> get
140158 put modul = do
src/AST/Pattern.hs+11−7
@@ -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
@@ -103,29 +107,29 @@ boundVarList pattern =
103107-- PRETTY PRINTING
104108
105109instance Var.ToString var => P.Pretty (Pattern' ann var) where
106− pretty needsParens pattern =
110+ pretty dealiaser needsParens pattern =
107111 case pattern of
108112 Var name ->
109113 P.text name
110114
111115 Literal literal ->
112− P.pretty needsParens literal
116+ P.pretty dealiaser needsParens literal
113117
114118 Record fields ->
115119 P.braces (P.commaCat (map P.text fields))
116120
117121 Alias x ptrn ->
118122 P.parensIf needsParens $
119− P.pretty True ptrn <+> P.text "as" <+> P.text x
123+ P.pretty dealiaser True ptrn <+> P.text "as" <+> P.text x
120124
121125 Anything ->
122126 P.text "_"
123127
124128 Data name [A.A _ hd, A.A _ tl]
125129 | Var.toString name == "::" ->
126− P.parensIf isCons (P.pretty False hd)
130+ P.parensIf isCons (P.pretty dealiaser False hd)
127131 <+> P.text "::"
128− <+> P.pretty False tl
132+ <+> P.pretty dealiaser False tl
129133 where
130134 isCons =
131135 case hd of
@@ -137,7 +141,7 @@ instance Var.ToString var => P.Pretty (Pattern' ann var) where
137141 in
138142 if Help.isTuple name'
139143 then
140− P.parens (P.commaCat (map (P.pretty False) patterns))
144+ P.parens (P.commaCat (map (P.pretty dealiaser False) patterns))
141145 else
142146 P.parensIf needsParens $
143− P.hsep (P.text name' : map (P.pretty True) patterns)
147+ P.hsep (P.text name' : map (P.pretty dealiaser True) patterns)
src/AST/Type.hs+178−86
@@ -1,7 +1,7 @@
11module AST.Type
22 ( Raw, Raw'(..)
33 , Canonical(..), Aliased(..)
4− , Port(..), portType
4+ , Port(..), getPortType
55 , deepDealias, dealias
66 , collectLambdas
77 , fieldMap
@@ -12,6 +12,7 @@ import Control.Applicative ((<$>), (<*>))
1212import Control.Arrow (second)
1313import Data.Binary
1414import qualified Data.Map as Map
15+import qualified Data.Maybe as Maybe
1516import Text.PrettyPrint as P
1617
1718import qualified AST.Variable as Var
@@ -58,8 +59,8 @@ data Port t
5859 deriving (Show)
5960
6061
61−portType :: Port tipe -> tipe
62−portType portType =
62+getPortType :: Port tipe -> tipe
63+getPortType portType =
6364 case portType of
6465 Normal tipe -> tipe
6566 Signal tipe _ -> tipe
@@ -151,119 +152,210 @@ dealiasHelp typeTable tipe =
151152-- PRETTY PRINTING
152153
153154instance (P.Pretty t) => P.Pretty (Port t) where
154− pretty needsParens portType =
155− case portType of
156− Normal tipe ->
157− P.pretty needsParens tipe
158−
159− Signal tipe _ ->
160− P.pretty needsParens tipe
155+ pretty dealiaser needsParens portType =
156+ P.pretty dealiaser needsParens (getPortType portType)
161157
162158
163159instance P.Pretty Raw' where
164− pretty _ tipe =
165− P.text (show tipe)
160+ pretty dealiaser needsParens tipe =
161+ case tipe of
162+ RLambda arg body ->
163+ P.parensIf needsParens (prettyLambda dealiaser getRawLambda arg body)
164+
165+ RVar x ->
166+ P.text x
167+
168+ RType var ->
169+ prettyType dealiaser var
170+
171+ RApp func args ->
172+ let
173+ isTuple (A.A _ (RType name)) = Help.isTuple (Var.toString name)
174+ isTuple _ = False
175+ in
176+ prettyApp dealiaser needsParens isTuple func args
177+
178+ RRecord fields ext ->
179+ prettyRecord dealiaser (flattenRawRecord fields ext)
166180
167181
168182instance P.Pretty Canonical where
169− pretty needsParens tipe =
183+ pretty dealiaser needsParens tipe =
170184 case tipe of
171− Lambda _ _ ->
172− P.parensIf needsParens $
173− P.sep [ t, P.sep (map (P.text "->" <+>) ts) ]
174− where
175− t:ts =
176− map prettyLambda (collectLambdas tipe)
177−
178− prettyLambda t =
179− case t of
180− Lambda _ _ ->
181− P.pretty True t
182− _ ->
183− P.pretty False t
185+ Lambda arg body ->
186+ P.parensIf needsParens (prettyLambda dealiaser getCanLambda arg body)
184187
185188 Var x ->
186189 P.text x
187190
188191 Type var ->
189− let v = Var.toString var
192+ prettyType dealiaser var
193+
194+ App func args ->
195+ let
196+ isTuple (Type name) = Help.isTuple (Var.toString name)
197+ isTuple _ = False
190198 in
191− P.text (if v == "_Tuple0" then "()" else v)
199+ prettyApp dealiaser needsParens isTuple func args
192200
193− App f args ->
194− case (f,args) of
195− (Type name, _)
196− | Help.isTuple (Var.toString name) ->
197− P.parens $ P.sep $
198− P.punctuate P.comma (map (P.pretty False) args)
199−
200− (_, []) ->
201− P.pretty True f
202−
203− (_, _) ->
204− P.parensIf needsParens $
205− P.hang
206− (P.pretty True f)
207− 2
208− (P.sep (map (P.pretty True) args))
209−
210− Record _ _ ->
211− case flattenRecord tipe of
212− ([], Nothing) ->
213− P.text "{}"
214−
215− (fields, Nothing) ->
216− P.sep
217− [ P.cat (zipWith (<+>) (P.lbrace : repeat P.comma) (map prettyField fields))
218− , P.rbrace
219− ]
220−
221− (fields, Just x) ->
222− P.hang
223− (P.lbrace <+> P.text x <+> P.text "|")
224− 4
225− (P.sep
226− [ P.cat (zipWith (<+>) (P.space : repeat P.comma) (map prettyField fields))
227− , P.rbrace
228− ])
229− where
230− prettyField (field, tipe) =
231− P.text field <+> P.text ":" <+> P.pretty False tipe
201+ Record fields ext ->
202+ prettyRecord dealiaser (flattenCanRecord fields ext)
232203
233204 Aliased name args _ ->
234205 P.parensIf (needsParens && not (null args)) $
235206 P.hang
236− (P.pretty False name)
207+ (P.pretty dealiaser False name)
237208 2
238− (P.sep (map (P.pretty True . snd) args))
209+ (P.sep (map (P.pretty dealiaser True . snd) args))
239210
240211
241−collectLambdas :: Canonical -> [Canonical]
242−collectLambdas tipe =
212+-- PRETTY HELPERS
213+
214+prettyType :: (Var.ToString var) => P.Dealiaser -> var -> P.Doc
215+prettyType dealiaser var =
216+ let
217+ v = Var.toString var
218+ in
219+ P.text $
220+ if v == "_Tuple0" then
221+ "()"
222+ else
223+ maybe v id (Map.lookup v dealiaser)
224+
225+
226+-- PRETTY LAMBDAS
227+
228+prettyLambda :: (P.Pretty t) => P.Dealiaser -> (t -> Maybe (t,t)) -> t -> t -> P.Doc
229+prettyLambda dealiaser getLambda arg body =
230+ let
231+ rest =
232+ gatherLambda getLambda body
233+
234+ prettyArg t =
235+ P.pretty dealiaser (Maybe.isJust (getLambda t)) t
236+ in
237+ P.sep
238+ [ prettyArg arg
239+ , P.sep (map (\t -> P.text "->" <+> prettyArg t) rest)
240+ ]
241+
242+
243+getRawLambda :: Raw -> Maybe (Raw, Raw)
244+getRawLambda (A.A _ tipe) =
243245 case tipe of
244− Lambda arg body ->
245− arg : collectLambdas body
246+ RLambda arg body -> Just (arg, body)
247+ _ -> Nothing
246248
247− _ ->
249+
250+getCanLambda :: Canonical -> Maybe (Canonical, Canonical)
251+getCanLambda tipe =
252+ case tipe of
253+ Lambda arg body -> Just (arg, body)
254+ _ -> Nothing
255+
256+
257+gatherLambda :: (t -> Maybe (t,t)) -> t -> [t]
258+gatherLambda get tipe =
259+ case get tipe of
260+ Just (arg, body) ->
261+ arg : gatherLambda get body
262+
263+ Nothing ->
248264 [tipe]
249265
250266
251−flattenRecord :: Canonical -> ( [(String, Canonical)], Maybe String )
252−flattenRecord tipe =
253− case tipe of
254− Var x ->
255− ([], Just x)
267+collectLambdas :: Canonical -> [Canonical]
268+collectLambdas tipe =
269+ gatherLambda getCanLambda tipe
270+
271+
272+-- PRETTY APP
273+
274+prettyApp :: (P.Pretty t) => P.Dealiaser -> Bool -> (t -> Bool) -> t -> [t] -> P.Doc
275+prettyApp dealiaser needsParens isTuple func args
276+ | isTuple func =
277+ P.parens $ P.sep $
278+ P.punctuate P.comma (map (P.pretty dealiaser False) args)
279+
280+ | null args =
281+ P.pretty dealiaser needsParens func
282+
283+ | otherwise =
284+ P.parensIf needsParens $
285+ P.hang
286+ (P.pretty dealiaser True func)
287+ 2
288+ (P.sep (map (P.pretty dealiaser True) args))
256289
257− Record fields Nothing ->
290+
291+-- PRETTY RECORD
292+
293+prettyRecord :: (P.Pretty t) => P.Dealiaser -> ([(String, t)], Maybe String) -> P.Doc
294+prettyRecord dealiaser recordInfo =
295+ let
296+ prettyField (field, tipe) =
297+ P.hang
298+ (P.text field <+> P.text ":")
299+ 4
300+ (P.pretty dealiaser False tipe)
301+ in
302+ case recordInfo of
303+ ([], Nothing) ->
304+ P.text "{}"
305+
306+ (fields, Nothing) ->
307+ P.sep
308+ [ P.cat (zipWith (<+>) (P.lbrace : repeat P.comma) (map prettyField fields))
309+ , P.rbrace
310+ ]
311+
312+ (fields, Just x) ->
313+ P.hang
314+ (P.lbrace <+> P.text x <+> P.text "|")
315+ 4
316+ (P.sep
317+ [ P.sep (P.punctuate P.comma (map prettyField fields))
318+ , P.rbrace
319+ ]
320+ )
321+
322+
323+flattenRawRecord
324+ :: [(String, Raw)]
325+ -> Maybe Raw
326+ -> ( [(String, Raw)], Maybe String )
327+flattenRawRecord fields ext =
328+ case ext of
329+ Nothing ->
330+ (fields, Nothing)
331+
332+ Just (A.A _ (RVar x)) ->
333+ (fields, Just x)
334+
335+ Just (A.A _ (RRecord fields' ext')) ->
336+ flattenRawRecord (fields' ++ fields) ext'
337+
338+ _ ->
339+ error "Trying to flatten ill-formed record."
340+
341+
342+flattenCanRecord
343+ :: [(String, Canonical)]
344+ -> Maybe Canonical
345+ -> ( [(String, Canonical)], Maybe String )
346+flattenCanRecord fields ext =
347+ case ext of
348+ Nothing ->
258349 (fields, Nothing)
259350
260− Record fields (Just ext) ->
261− let (fields',ext') = flattenRecord ext
262− in
263− (fields' ++ fields, ext')
351+ Just (Var x) ->
352+ (fields, Just x)
353+
354+ Just (Record fields' ext') ->
355+ flattenCanRecord (fields' ++ fields) ext'
264356
265− Aliased _ args tipe' ->
266− flattenRecord (dealias args tipe')
357+ Just (Aliased _ args tipe) ->
358+ flattenCanRecord fields (Just (dealias args tipe))
267359
268360 _ ->
269361 error "Trying to flatten ill-formed record."
src/AST/Variable.hs+11−7
@@ -3,6 +3,7 @@ module AST.Variable where
33import Control.Applicative ((<$>), (<*>))
44import Data.Binary
55import qualified Data.List as List
6+import qualified Data.Map as Map
67import qualified Data.Maybe as Maybe
78import Text.PrettyPrint as P
89
@@ -203,26 +204,29 @@ getUnion value =
203204-- PRETTY VARIABLES
204205
205206instance P.Pretty Raw where
206− pretty _ (Raw name) =
207+ pretty _ _ (Raw name) =
207208 if Help.isOp name
208209 then P.parens (P.text name)
209210 else P.text name
210211
211212
212213instance P.Pretty Canonical where
213− pretty _ var =
214− P.text (toString var)
214+ pretty dealiaser _ var =
215+ let
216+ name = toString var
217+ in
218+ P.text (maybe name id (Map.lookup name dealiaser))
215219
216220
217221instance P.Pretty a => P.Pretty (Listing a) where
218− pretty _ (Listing explicits open) =
222+ pretty dealiaser _ (Listing explicits open) =
219223 let dots = [if open then P.text ".." else P.empty]
220224 in
221− P.parens (P.commaCat (map (P.pretty False) explicits ++ dots))
225+ P.parens (P.commaCat (map (P.pretty dealiaser False) explicits ++ dots))
222226
223227
224228instance P.Pretty Value where
225− pretty _ portable =
229+ pretty dealiaser _ portable =
226230 case portable of
227231 Value name ->
228232 P.text name
@@ -231,7 +235,7 @@ instance P.Pretty Value where
231235 P.text name
232236
233237 Union name ctors ->
234− P.text name <> P.pretty False ctors
238+ P.text name <> P.pretty dealiaser False ctors
235239
236240
237241-- BINARY SERIALIZATION
src/Canonicalize.hs+150−92
@@ -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
@@ -18,9 +18,12 @@ import qualified AST.Module as Module
1818import qualified AST.Pattern as P
1919import qualified AST.Type as Type
2020import qualified AST.Variable as Var
21+import qualified Docs.AST as Docs
22+import qualified Docs.Centralize as Docs
2123import qualified Reporting.Annotation as A
2224import qualified Reporting.Error as Error
2325import qualified Reporting.Error.Canonicalize as CError
26+import qualified Reporting.Error.Helpers as ErrorHelp
2427import qualified Reporting.Result as R
2528import qualified Reporting.Warning as Warning
2629import qualified Canonicalize.Declaration as Decls
@@ -39,35 +42,42 @@ module'
3942 :: Module.Interfaces
4043 -> Module.ValidModule
4144 -> R.Result Warning.Warning Error.Error Module.CanonicalModule
42−module' interfaces modul@(Module.Module _ _ _ (_, imports) _) =
43− let (Result.Result uses rawResults) =
44− moduleHelp interfaces modul
45−
46− unusedImportWarnings =
47− Maybe.mapMaybe (warnUnusedImport uses) imports
45+module' interfaces modul =
46+ let
47+ (Result.Result uses rawResults) =
48+ moduleHelp interfaces modul
4849 in
49− R.addWarnings unusedImportWarnings $
50− case rawResults of
51− Result.Ok canonicalModule ->
52− return canonicalModule
50+ case rawResults of
51+ Result.Ok (env, almostCanonicalModule) ->
52+ R.addDealiaser (Env.toDealiaser env) $
53+ filterImports uses almostCanonicalModule
54+
55+ Result.Err msgs ->
56+ R.throwMany (map (A.map Error.Canonicalize) msgs)
57+
5358
54− Result.Err msgs ->
55− R.throwMany (map (A.map Error.Canonicalize) msgs)
59+type AlmostCanonicalModule =
60+ Module.Module
61+ Docs.Centralized
62+ ([Module.DefaultImport], [Module.UserImport])
63+ [Var.Value]
64+ (Module.Body Canonical.Expr)
5665
5766
5867moduleHelp
5968 :: Module.Interfaces
6069 -> Module.ValidModule
61− -> Result.ResultErr Module.CanonicalModule
62−moduleHelp interfaces modul@(Module.Module _ _ exports (defaults, imports) decls) =
70+ -> Result.ResultErr (Env.Environment, AlmostCanonicalModule)
71+moduleHelp interfaces modul@(Module.Module _ _ comment exports _ decls) =
6372 canonicalModule
6473 <$> canonicalDeclsResult
6574 <*> resolveExports locals exports
6675 where
67− canonicalModule canonicalDecls canonicalExports =
76+ canonicalModule (env, canonicalDecls) canonicalExports =
77+ (,) env $
6878 modul
69− { Module.exports = canonicalExports
70− , Module.imports = map fst defaults ++ map (fst . A.drop) imports
79+ { Module.docs = A.map (fmap (Docs.centralize canonicalDecls)) comment
80+ , Module.exports = canonicalExports
7181 , Module.body = body canonicalDecls
7282 }
7383
@@ -77,13 +87,13 @@ moduleHelp interfaces modul@(Module.Module _ _ exports (defaults, imports) decls
7787
7888 canonicalDeclsResult =
7989 Setup.environment interfaces modul
80− `Result.andThen` \env -> T.traverse (declaration env) decls
90+ `Result.andThen` \env -> (,) env <$> T.traverse (declaration env) decls
8191
82− body :: [D.CanonicalDecl] -> Module.CanonicalBody
92+ body :: [D.CanonicalDecl] -> Module.Body Canonical.Expr
8393 body decls =
8494 let nakedDecls = map A.drop decls
8595 in
86− Module.CanonicalBody
96+ Module.Body
8797 { program =
8898 let expr = Decls.toExpr (Module.names modul) decls
8999 in
@@ -108,92 +118,130 @@ moduleHelp interfaces modul@(Module.Module _ _ exports (defaults, imports) decls
108118
109119-- IMPORTS / EXPORTS
110120
111−warnUnusedImport
121+filterImports
112122 :: Set.Set Module.Name
113− -> Module.UserImport
114− -> Maybe (A.Located Warning.Warning)
115−warnUnusedImport uses (A.A region (name, _method)) =
116− if Set.member name uses
117− then Nothing
118− else Just (A.A region (Warning.UnusedImport name))
123+ -> AlmostCanonicalModule
124+ -> R.Result Warning.Warning e Module.CanonicalModule
125+filterImports uses modul@(Module.Module _ _ _ _ (defaults, imports) _) =
126+ do reducedImports <-
127+ Maybe.catMaybes <$> T.traverse checkImport imports
128+
129+ return $ modul
130+ { Module.imports =
131+ Set.toList (Set.fromList (map fst defaults ++ reducedImports))
132+ }
133+ where
134+ checkImport (A.A region (name, _method)) =
135+ if Set.member name uses
136+ then
137+ return (Just name)
138+ else
139+ do R.warn region (Warning.UnusedImport name)
140+ return Nothing
119141
120142
121143resolveExports
122144 :: [Var.Value]
123− -> Var.Listing Var.Value
145+ -> Var.Listing (A.Located Var.Value)
124146 -> Result.ResultErr [Var.Value]
125147resolveExports fullList (Var.Listing partialList open) =
126148 if open
127149 then Result.ok fullList
128150 else
129151 (\xs ys zs -> xs ++ ys ++ zs)
130− <$> getValueExports
152+ <$> T.traverse getValueExport values
131153 <*> (concat <$> T.traverse getAliasExport aliases)
132154 <*> T.traverse getAdtExport adts
133155 where
134156 (allValues, allAliases, allAdts) =
135− splitValues fullList
157+ maybeUnzip3 (map splitValue fullList)
136158
137159 (values, aliases, adts) =
138− splitValues partialList
160+ maybeUnzip3 (map splitLocatedValue partialList)
161+
162+ allValuesSet = Set.fromList allValues
163+ adtTypes = map fst allAdts
139164
140− getValueExports =
141− case Set.toList (Set.difference values' allValues') of
142− [] -> Result.ok (map Var.Value values)
143− xs -> manyNotFound xs allValues
144− where
145− allValues' = Set.fromList allValues
146− values' = Set.fromList values
165+ getValueExport (A.A region name) =
166+ if Set.member name allValuesSet
167+ then Result.ok (Var.Value name)
168+ else manyNotFound region [name] allValues
147169
148− getAliasExport alias
170+ getAliasExport (A.A region alias)
149171 | alias `elem` allAliases =
150− let recordConstructor =
151− if alias `elem` allValues then [Var.Value alias] else []
152− in
153− Result.ok (Var.Alias alias : recordConstructor)
172+ Result.ok $ (:) (Var.Alias alias) $
173+ if alias `elem` allValues then [Var.Value alias] else []
174+
175+ | List.elem alias adtTypes =
176+ Result.ok [Var.Union alias (Var.Listing [] False)]
154177
155178 | otherwise =
156− case List.find (\(name, _ctors) -> name == alias) allAdts of
157− Nothing -> manyNotFound [alias] allAliases
158− Just (name, _ctor) ->
159− Result.ok [Var.Union name (Var.Listing [] False)]
160−
161− getAdtExport (name, Var.Listing ctors open) =
162− case lookup name allAdts of
163− Nothing -> manyNotFound [name] (map fst allAdts)
179+ manyNotFound region [alias] (allAliases ++ adtTypes)
180+
181+ getAdtExport (A.A region (name, Var.Listing ctors open)) =
182+ case List.lookup name allAdts of
183+ Nothing ->
184+ manyNotFound region [name] adtTypes
185+
164186 Just (Var.Listing allCtors _)
165187 | open ->
166188 Result.ok (Var.Union name (Var.Listing allCtors False))
167189 | otherwise ->
168190 case filter (`notElem` allCtors) ctors of
169− [] -> Result.ok (Var.Union name (Var.Listing ctors False))
170− unfoundCtors -> manyNotFound unfoundCtors allCtors
191+ [] ->
192+ Result.ok (Var.Union name (Var.Listing ctors False))
193+ unfoundCtors ->
194+ manyNotFound region unfoundCtors allCtors
171195
172− manyNotFound nameList possibilities =
173− Result.errors (map (notFound possibilities) nameList)
196+ manyNotFound region nameList possibilities =
197+ Result.errors (map (notFound region possibilities) nameList)
174198
175− notFound possibilities name =
176− A.A undefined $ CError.Export name $
177− CError.nearbyNames id name possibilities
199+ notFound region possibilities name =
200+ A.A region $ CError.Export name $
201+ ErrorHelp.nearbyNames id name possibilities
178202
179203
180−splitValues
181− :: [Var.Value]
182− -> ([String], [String], [(String, Var.Listing String)])
183−splitValues mixedValues =
184− case mixedValues of
185− [] -> ([], [], [])
186− x:xs ->
187− let (values, aliases, adts) = splitValues xs in
188− case x of
189− Var.Value name ->
190− (name : values, aliases, adts)
204+-- GROUPING VALUES
205+
206+maybeUnzip3 :: [(Maybe a, Maybe b, Maybe c)] -> ([a],[b],[c])
207+maybeUnzip3 tuples =
208+ let (as, bs, cs) = unzip3 tuples
209+ in
210+ (Maybe.catMaybes as, Maybe.catMaybes bs, Maybe.catMaybes cs)
211+
191212
192− Var.Alias name ->
193− (values, name : aliases, adts)
213+splitValue
214+ :: Var.Value
215+ -> ( Maybe String, Maybe String, Maybe (String, Var.Listing String) )
216+splitValue value =
217+ case value of
218+ Var.Value name ->
219+ (Just name, Nothing, Nothing)
194220
195− Var.Union name listing ->
196− (values, aliases, (name, listing) : adts)
221+ Var.Alias name ->
222+ (Nothing, Just name, Nothing)
223+
224+ Var.Union name listing ->
225+ (Nothing, Nothing, Just (name, listing))
226+
227+
228+splitLocatedValue
229+ :: A.Located Var.Value
230+ ->
231+ ( Maybe (A.Located String)
232+ , Maybe (A.Located String)
233+ , Maybe (A.Located (String, Var.Listing String))
234+ )
235+splitLocatedValue (A.A region value) =
236+ case value of
237+ Var.Value name ->
238+ (Just (A.A region name), Nothing, Nothing)
239+
240+ Var.Alias name ->
241+ (Nothing, Just (A.A region name), Nothing)
242+
243+ Var.Union name listing ->
244+ (Nothing, Nothing, Just (A.A region (name, listing)))
197245
198246
199247-- DECLARATIONS
@@ -220,53 +268,58 @@ declaration
220268 :: Env.Environment
221269 -> D.ValidDecl
222270 -> Result.ResultErr D.CanonicalDecl
223−declaration env (A.A region decl) =
224− let canonicalize kind _context _pattern env v =
225− kind env v
226− in
227− A.A region <$>
271+declaration env (A.A ann@(region,_) decl) =
272+ A.A ann <$>
228273 case decl of
229− D.Definition (Valid.Definition p e t) ->
274+ D.Definition (Valid.Definition pat expr typ) ->
230275 D.Definition <$> (
231276 Canonical.Definition
232− <$> canonicalize pattern "definition" p env p
233− <*> expression env e
234− <*> T.traverse (canonicalize Canonicalize.tipe "definition" p env) t
277+ <$> pattern env pat
278+ <*> expression env expr
279+ <*> T.traverse (regionType env) typ
235280 )
236281
237282 D.Datatype name tvars ctors ->
238283 D.Datatype name tvars <$> T.traverse canonicalize' ctors
239284 where
240285 canonicalize' (ctor,args) =
241− (,) ctor <$> T.traverse (canonicalize Canonicalize.tipe "datatype" name env) args
286+ (,) ctor <$> T.traverse (Canonicalize.tipe env) args
242287
243288 D.TypeAlias name tvars expanded ->
244289 D.TypeAlias name tvars
245− <$> canonicalize Canonicalize.tipe "type alias" name env expanded
290+ <$> Canonicalize.tipe env expanded
246291
247292 D.Port validPort ->
248293 Result.addModule ["Native","Port"] $
249294 Result.addModule ["Native","Json"] $
250295 case validPort of
251296 D.In name tipe ->
252− canonicalize Canonicalize.tipe "port" name env tipe
297+ Canonicalize.tipe env tipe
253298 `Result.andThen` \canonicalType ->
254− D.Port <$> Port.check name Nothing canonicalType
299+ D.Port <$> Port.check region name Nothing canonicalType
255300
256301 D.Out name expr tipe ->
257302 let exprTypeResult =
258303 (,)
259304 <$> expression env expr
260− <*> canonicalize Canonicalize.tipe "port" name env tipe
305+ <*> Canonicalize.tipe env tipe
261306 in
262307 exprTypeResult
263308 `Result.andThen` \(expr', tipe') ->
264− D.Port <$> Port.check name (Just expr') tipe'
309+ D.Port <$> Port.check region name (Just expr') tipe'
265310
266311 D.Fixity assoc prec op ->
267312 Result.ok (D.Fixity assoc prec op)
268313
269314
315+regionType
316+ :: Env.Environment
317+ -> Type.Raw
318+ -> Result.ResultErr (A.Located Type.Canonical)
319+regionType env typ@(A.A region _) =
320+ A.A region <$> Canonicalize.tipe env typ
321+
322+
270323expression
271324 :: Env.Environment
272325 -> Valid.Expr
@@ -314,8 +367,10 @@ expression env (A.A region validExpr) =
314367 App func arg ->
315368 App <$> go func <*> go arg
316369
317− MultiIf branches ->
318− MultiIf <$> T.traverse go' branches
370+ MultiIf branches finally ->
371+ MultiIf
372+ <$> T.traverse go' branches
373+ <*> go finally
319374 where
320375 go' (condition, branch) =
321376 (,) <$> go condition <*> go branch
@@ -330,7 +385,7 @@ expression env (A.A region validExpr) =
330385 Canonical.Definition
331386 <$> pattern env' p
332387 <*> expression env' body
333− <*> T.traverse (Canonicalize.tipe env') mtipe
388+ <*> T.traverse (regionType env') mtipe
334389
335390 Var (Var.Raw x) ->
336391 Var <$> Canonicalize.variable region env x
@@ -374,6 +429,9 @@ expression env (A.A region validExpr) =
374429 GLShader uid src tipe ->
375430 Result.ok (GLShader uid src tipe)
376431
432+ Crash details ->
433+ Result.ok (Crash details)
434+
377435
378436pattern
379437 :: Env.Environment
src/Canonicalize/Declaration.hs+10−9
@@ -9,6 +9,7 @@ import qualified AST.Pattern as P
99import qualified AST.Type as T
1010import qualified AST.Variable as Var
1111import qualified Reporting.Annotation as A
12+import qualified Reporting.Region as R
1213
1314
1415toExpr :: Module.Name -> [D.CanonicalDecl] -> [Canonical.Def]
@@ -17,7 +18,7 @@ toExpr moduleName decls =
1718
1819
1920toDefs :: Module.Name -> D.CanonicalDecl -> [Canonical.Def]
20−toDefs moduleName (A.A region decl) =
21+toDefs moduleName (A.A (region,_) decl) =
2122 let typeVar = Var.fromModule moduleName
2223
2324 loc expr =
@@ -35,10 +36,10 @@ toDefs moduleName (A.A region decl) =
3536 tbody = T.App (T.Type (typeVar name)) (map T.Var tvars)
3637 body = loc . E.Data ctor $ map (loc . E.localVar) vars
3738 in
38− [ definition ctor (buildFunction body vars) (foldr T.Lambda tbody tipes) ]
39+ [ definition ctor (buildFunction body vars) region (foldr T.Lambda tbody tipes) ]
3940
4041 D.TypeAlias name tvars tipe@(T.Record fields ext) ->
41− [ definition name (buildFunction record vars) (foldr T.Lambda result args) ]
42+ [ definition name (buildFunction record vars) region (foldr T.Lambda result args) ]
4243 where
4344 result =
4445 T.Aliased (typeVar name) (zip tvars (map T.Var tvars)) (T.Holey tipe)
@@ -70,13 +71,13 @@ toDefs moduleName (A.A region decl) =
7071 in
7172 case impl of
7273 E.In name tipe ->
73− [ definition name body (T.portType tipe) ]
74+ [ definition name body region (T.getPortType tipe) ]
7475
7576 E.Out name _expr tipe ->
76− [ definition name body (T.portType tipe) ]
77+ [ definition name body region (T.getPortType tipe) ]
7778
7879 E.Task name _expr tipe ->
79− [ definition name body (T.portType tipe) ]
80+ [ definition name body region (T.getPortType tipe) ]
8081
8182 -- no constraints are needed for fixity declarations
8283 D.Fixity _ _ _ ->
@@ -96,6 +97,6 @@ buildFunction body@(A.A ann _) vars =
9697 (map (A.A ann . P.Var) vars)
9798
9899
99−definition :: String -> Canonical.Expr -> T.Canonical -> Canonical.Def
100−definition name expr@(A.A ann _) tipe =
101− Canonical.Definition (A.A ann (P.Var name)) expr (Just tipe)
100+definition :: String -> Canonical.Expr -> R.Region -> T.Canonical -> Canonical.Def
101+definition name expr@(A.A ann _) region tipe =
102+ Canonical.Definition (A.A ann (P.Var name)) expr (Just (A.A region tipe))
src/Canonicalize/Environment.hs+34
@@ -3,6 +3,7 @@ module Canonicalize.Environment where
33
44import qualified Data.List as List
55import qualified Data.Map as Map
6+import qualified Data.Maybe as Maybe
67import qualified Data.Set as Set
78
89import AST.Expression.General (saveEnvName)
@@ -10,6 +11,7 @@ import qualified AST.Module as Module
1011import qualified AST.Pattern as P
1112import qualified AST.Type as Type
1213import qualified AST.Variable as Var
14+import Elm.Utils ((|>))
1315
1416
1517-- ENVIRONMENT
@@ -104,3 +106,35 @@ builtinPatches =
104106 toTuple :: Int -> (String, Int)
105107 toTuple n =
106108 ("_Tuple" ++ show n, n)
109+
110+
111+-- TO TYPE DEALIASER
112+
113+toDealiaser :: Environment -> Map.Map String String
114+toDealiaser (Env _ _ adts aliases _) =
115+ let
116+ dealiasAdt (localName, canonicalSet) =
117+ case Set.toList canonicalSet of
118+ [canonicalName] ->
119+ Just (Var.toString canonicalName, localName)
120+ _ ->
121+ Nothing
122+
123+ dealiasAlias (localName, canonicalSet) =
124+ case Set.toList canonicalSet of
125+ [(canonicalName,_,_)] ->
126+ Just (Var.toString canonicalName, localName)
127+ _ ->
128+ Nothing
129+
130+ adtPairs =
131+ Maybe.mapMaybe dealiasAdt (Map.toList adts)
132+
133+ aliasPairs =
134+ Maybe.mapMaybe dealiasAlias (Map.toList aliases)
135+
136+ add (key,value) dict =
137+ Map.insertWith (\v v' -> if length v < length v' then v else v') key value dict
138+ in
139+ adtPairs ++ aliasPairs
140+ |> foldr add Map.empty
src/Canonicalize/Port.hs+18−14
@@ -10,30 +10,33 @@ import qualified AST.Type as T
1010import qualified AST.Variable as Var
1111import qualified Reporting.Annotation as A
1212import qualified Reporting.Error.Canonicalize as Error
13+import qualified Reporting.Region as R
1314import qualified Canonicalize.Result as Result
1415
1516
1617-- CHECK FOR PORT
1718
1819check
19− :: String
20+ :: R.Region
21+ -> String
2022 -> Maybe Canonical.Expr
2123 -> T.Canonical
2224 -> Result.ResultErr D.CanonicalPort
23−check name maybeExpr rootType =
24− D.CanonicalPort <$> checkHelp name maybeExpr rootType rootType
25+check region name maybeExpr rootType =
26+ D.CanonicalPort <$> checkHelp region name maybeExpr rootType rootType
2527
2628
2729checkHelp
28− :: String
30+ :: R.Region
31+ -> String
2932 -> Maybe Canonical.Expr
3033 -> T.Canonical
3134 -> T.Canonical
3235 -> Result.ResultErr (E.PortImpl Canonical.Expr T.Canonical)
33−checkHelp name maybeExpr rootType tipe =
36+checkHelp region name maybeExpr rootType tipe =
3437 case (maybeExpr, tipe) of
3538 (_, T.Aliased _ args t) ->
36− checkHelp name maybeExpr rootType (T.dealias args t)
39+ checkHelp region name maybeExpr rootType (T.dealias args t)
3740
3841 (Just expr, T.App (T.Type task) [ _x, _a ])
3942 | Var.isTask task ->
@@ -50,37 +53,38 @@ checkHelp name maybeExpr rootType tipe =
5053 case maybeExpr of
5154 Nothing ->
5255 const (E.In name (T.Signal rootType arg))
53− <$> validForeignType name True arg arg
56+ <$> validForeignType region name True arg arg
5457
5558 Just expr ->
5659 const (E.Out name expr (T.Signal rootType arg))
57− <$> validForeignType name False arg arg
60+ <$> validForeignType region name False arg arg
5861
5962 _ ->
6063 case maybeExpr of
6164 Nothing ->
6265 const (E.In name (T.Normal rootType))
63− <$> validForeignType name True rootType tipe
66+ <$> validForeignType region name True rootType tipe
6467
6568 Just expr ->
6669 const (E.Out name expr (T.Normal rootType))
67− <$> validForeignType name False rootType tipe
70+ <$> validForeignType region name False rootType tipe
6871
6972
7073-- CHECK INBOUND AND OUTBOUND TYPES
7174
7275validForeignType
73− :: String
76+ :: R.Region
77+ -> String
7478 -> Bool
7579 -> T.Canonical
7680 -> T.Canonical
7781 -> Result.ResultErr ()
78−validForeignType name isInbound rootType tipe =
82+validForeignType region name isInbound rootType tipe =
7983 let valid localType =
80− validForeignType name isInbound rootType localType
84+ validForeignType region name isInbound rootType localType
8185
8286 err problem =
83− Result.err $ A.A undefined $
87+ Result.err $ A.A region $
8488 Error.port name isInbound rootType tipe problem
8589 in
8690 case tipe of
src/Canonicalize/Setup.hs+3−2
@@ -17,6 +17,7 @@ import qualified AST.Variable as Var
1717import Elm.Utils ((|>))
1818import qualified Reporting.Annotation as A
1919import qualified Reporting.Error.Canonicalize as Error
20+import qualified Reporting.Error.Helpers as Error
2021import qualified Reporting.Region as R
2122import qualified Canonicalize.Environment as Env
2223import qualified Canonicalize.Result as Result
@@ -27,7 +28,7 @@ environment
2728 :: Module.Interfaces
2829 -> Module.ValidModule
2930 -> Result.ResultErr Env.Environment
30−environment interfaces modul@(Module.Module _ _ _ (defaults, imports) decls) =
31+environment interfaces modul@(Module.Module _ _ _ _ (defaults, imports) decls) =
3132 let moduleName =
3233 Module.names modul
3334
@@ -288,7 +289,7 @@ declToPatches
288289 :: Module.Name
289290 -> D.ValidDecl
290291 -> (Maybe Node, [Env.Patch])
291−declToPatches moduleName (A.A region decl) =
292+declToPatches moduleName (A.A (region,_) decl) =
292293 let local mkPatch x =
293294 mkPatch x (Var.local x)
294295
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/Canonicalize/Variable.hs+3−2
@@ -11,6 +11,7 @@ import qualified AST.Type as Type
1111import qualified AST.Variable as Var
1212import qualified Reporting.Annotation as A
1313import qualified Reporting.Error.Canonicalize as Error
14+import qualified Reporting.Error.Helpers as Error
1415import qualified Reporting.Region as R
1516import qualified Canonicalize.Environment as Env
1617import qualified Canonicalize.Result as Result
@@ -211,7 +212,7 @@ qualifiedProblem moduleName name allQualified =
211212 in
212213 case Set.member moduleName availableModules of
213214 True ->
214− ( Error.QualifiedUnknown moduleNameString
215+ ( Error.QualifiedUnknown moduleNameString name
215216 , allQualified
216217 |> filter ((==) moduleName . fst)
217218 |> map snd
@@ -219,7 +220,7 @@ qualifiedProblem moduleName name allQualified =
219220 )
220221
221222 False ->
222− ( Error.UnknownQualifier moduleNameString
223+ ( Error.UnknownQualifier moduleNameString name
223224 , Set.toList availableModules
224225 |> map Module.nameToString
225226 |> Error.nearbyNames id moduleNameString
src/CommandLineRouter.hs+2−2
@@ -26,10 +26,10 @@ attemptToRun command args =
2626 case found of
2727 Nothing ->
2828 hPutStrLn stderr $
29− "Could not find command '" ++ command ++ "'. Maybe there is a typo?\n\n\
29+ "Could not find command `" ++ command ++ "`. Maybe there is a typo?\n\n\
3030 \Default commands include:\n\n"
3131 ++ availableCommands ++
32− "\nWhen you try to run the command '" ++ command ++ "' we actually search for an\n\
32+ "\nWhen you try to run the command `" ++ command ++ "` we actually search for an\n\
3333 \ executable named elm-" ++ command ++ ". Are you able to run elm-" ++ command ++ "?\n\
3434 \ Is it on your PATH?"
3535
src/Compile.hs+9−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.TopLevelTypes as Nitpick
89import qualified Parse.Helpers as Parse
910import qualified Parse.Parse as Parse
1011import qualified Reporting.Error as Error
@@ -16,11 +17,12 @@ import qualified Type.Inference as TI
1617compile
1718 :: String
1819 -> String
20+ -> Bool
1921 -> Module.Interfaces
2022 -> String
2123 -> Result.Result Warning.Warning Error.Error Module.CanonicalModule
2224
23−compile user projectName interfaces source =
25+compile user projectName isRoot interfaces source =
2426 do
2527 -- determine if default imports should be added
2628 -- only elm-lang/core is exempt
@@ -30,7 +32,7 @@ compile user projectName interfaces source =
3032 -- Parse the source code
3133 validModule <-
3234 Result.mapError Error.Syntax $
33− Parse.program needsDefaults (getOpTable interfaces) source
35+ Parse.program needsDefaults isRoot (getOpTable interfaces) source
3436
3537 -- Canonicalize all variables, pinning down where they came from.
3638 canonicalModule <-
@@ -41,7 +43,11 @@ compile user projectName interfaces source =
4143 Result.from Error.Type $
4244 TI.infer interfaces canonicalModule
4345
44− -- Add the real list of tyes
46+ -- One last round of checks
47+ Result.mapError Error.Type $
48+ Nitpick.topLevelTypes types (Module.body validModule)
49+
50+ -- Add the real list of types
4551 let body = (Module.body canonicalModule) { Module.types = types }
4652
4753 return $ canonicalModule { Module.body = body }
src/Docs.hsdeleted−275
@@ -1,275 +0,0 @@
1−{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
2−module Main where
3−
4−import System.Console.CmdArgs
5−import System.Directory
6−import System.FilePath
7−import System.Exit
8−import System.IO
9−
10−import Control.Applicative ((<$>))
11−import Control.Arrow (second)
12−import Control.Monad (when)
13−import qualified Data.Aeson.Encode.Pretty as Json
14−import qualified Data.ByteString.Lazy.Char8 as BS
15−import qualified Data.Map as Map
16−import qualified Data.Maybe as Maybe
17−import qualified Data.Set as Set
18−
19−import qualified AST.Declaration as Decl
20−import qualified AST.Expression.Source as Source
21−import qualified AST.Variable as Var
22−
23−import Text.Parsec hiding (newline, spaces)
24−import qualified Parse.Declaration as Parse (typeDecl, infixDecl)
25−import qualified Parse.Expression as Parse (typeAnnotation)
26−import qualified Parse.Helpers as Parse
27−import qualified Parse.Module as Parse (header)
28−import qualified Elm.Compiler.Module as Module
29−import qualified Elm.Compiler.Type.Extract as Type
30−import qualified Elm.Docs as Docs
31−
32−
33−-- FLAGS
34−
35−data Flags = Flags
36− { input :: FilePath
37− , output :: Maybe FilePath
38− }
39− deriving (Data,Typeable,Show,Eq)
40−
41−
42−defaultFlags :: Flags
43−defaultFlags = Flags
44− { input = def &= args &= typ "FILE"
45−
46− , output = Nothing &= typFile
47− &= help "file name for generated JSON documentation"
48−
49− } &= help "Generate documentation for Elm"
50− &= summary "Generate documentation for Elm, (c) Evan Czaplicki"
51−
52−
53−-- GENERATE DOCUMENTATION
54−
55−main :: IO ()
56−main =
57− do flags <- cmdArgs defaultFlags
58− let inputFileName = input flags
59−
60− exists <- doesFileExist inputFileName
61− when (not exists) $
62− do hPutStrLn stderr $
63− if null inputFileName
64− then "Error: you must provide a file to document!"
65− else "Error: could not find a file named '" ++ inputFileName ++ "'"
66− exitFailure
67−
68− source <- readFile inputFileName
69− case Parse.iParse documentation source of
70− Right docs ->
71− let json = Json.encodePretty' config docs in
72− case output flags of
73− Nothing -> BS.putStrLn json
74− Just docPath ->
75− do createDirectoryIfMissing True (dropFileName docPath)
76− BS.writeFile docPath json
77−
78− Left err ->
79− do hPutStrLn stderr (show err)
80− exitFailure
81−
82−
83−config :: Json.Config
84−config =
85− Json.Config
86− { Json.confIndent = 2
87− , Json.confCompare = Json.keyOrder keys
88− }
89− where
90− keys =
91− [ "tag", "name", "comment", "aliases", "types"
92− , "values", "func", "args", "type", "cases"
93− ]
94−
95−
96−-- PARSE DOCUMENTATION
97−
98−documentation :: Parse.IParser Docs.Documentation
99−documentation =
100− do optional Parse.freshLine
101− (names, exports) <- Parse.header
102−
103− manyTill (string " " <|> Parse.newline <?> "more whitespace")
104− (lookAhead (string "{-|") <?> "module documentation comment")
105−
106− overview <- docComment
107−
108− decls <- allDeclarations
109−
110− let (aliases, unions, values) = categorizeDeclarations decls
111−
112− return $
113− Docs.Documentation
114− (Module.Name names)
115− overview
116− (filterAliases exports aliases)
117− (filterUnions exports unions)
118− (filterValues exports values)
119−
120−
121−docComment :: Parse.IParser String
122−docComment =
123− do try (string "{-|")
124− contents <- Parse.closeComment
125−
126− let reversed =
127− dropWhile (`elem` " \n\r") . drop 2 $ reverse contents
128−
129− return $ dropWhile (==' ') (reverse reversed)
130−
131−
132−allDeclarations :: Parse.IParser [(String, Decl.SourceDecl)]
133−allDeclarations =
134− Parse.onFreshLines (:) [] declaration
135−
136−
137−declaration :: Parse.IParser (String, Decl.SourceDecl)
138−declaration =
139− uncommentable <|> commented <|> uncommented ""
140− where
141− uncommentable =
142− (,) "" <$> Parse.infixDecl
143−
144− commented =
145− do comment <- docComment
146− Parse.freshLine
147− uncommented comment
148−
149− uncommented comment =
150− (,) comment
151− <$> choice [ Parse.typeDecl, Decl.Definition <$> Parse.typeAnnotation ]
152−
153−
154−-- CATEGORIZE DECLARATIONS
155−
156−data CategoryInfo = CategoryInfo
157− { aliases :: [Docs.Alias]
158− , unions :: [Docs.Union]
159− , values :: Map.Map String Docs.Value
160− , infixes :: Map.Map String (String, Int)
161− }
162−
163−
164−emptyInfo :: CategoryInfo
165−emptyInfo =
166− CategoryInfo [] [] Map.empty Map.empty
167−
168−
169−categorizeDeclarations :: [(String, Decl.SourceDecl)] -> ([Docs.Alias], [Docs.Union], [Docs.Value])
170−categorizeDeclarations decls =
171− (aliases, unions, Map.elems values)
172− where
173− (CategoryInfo aliases unions rawValues infixes) =
174− foldr collectInfo emptyInfo decls
175−
176− values =
177− Map.union
178− (Map.intersectionWith addInfixInfo rawValues infixes)
179− rawValues
180−
181−
182−addInfixInfo :: Docs.Value -> (String, Int) -> Docs.Value
183−addInfixInfo value infixInfo =
184− value { Docs.valueAssocPrec = Just infixInfo }
185−
186−
187−collectInfo :: (String, Decl.SourceDecl) -> CategoryInfo -> CategoryInfo
188−collectInfo (comment, decl) info =
189− case decl of
190− Decl.Definition def ->
191− case def of
192− Source.Definition _ _ ->
193− error (errorMessage "unannotated definitions")
194−
195− Source.TypeAnnotation name tipe ->
196− let value = Docs.Value name comment (Type.fromInternalType tipe) Nothing
197− in
198− info { values = Map.insert name value (values info) }
199−
200− Decl.Datatype name args cases ->
201− let cases' = map (second (map Type.fromInternalType)) cases
202− union = Docs.Union name comment args cases'
203− in
204− info { unions = union : unions info }
205−
206− Decl.TypeAlias name args tipe ->
207− let alias = Docs.Alias name comment args (Type.fromInternalType tipe)
208− in
209− info { aliases = alias : aliases info }
210−
211− Decl.Fixity assoc prec name ->
212− let infixInfo = (Decl.assocToString assoc, prec)
213− in
214− info { infixes = Map.insert name infixInfo (infixes info) }
215−
216− Decl.Port _ ->
217− error (errorMessage "ports")
218−
219−
220−errorMessage :: String -> String
221−errorMessage name =
222− "Unable to deal with annotations on " ++ name ++ " at this time.\n" ++
223− "Please report your use case to <https://github.com/elm-lang/elm-compiler/issues>"
224−
225−
226−-- FILTER OUT UNEXPORTED VALUES, ALIASES, and UNIONS
227−
228−filterValues :: Var.Listing Var.Value -> [Docs.Value] -> [Docs.Value]
229−filterValues (Var.Listing exports open) values =
230− case open of
231− True -> values
232− False ->
233− let names =
234− Set.fromList (Var.getValues exports)
235− in
236− filter (\value -> Set.member (Docs.valueName value) names) values
237−
238−
239−filterAliases :: Var.Listing Var.Value -> [Docs.Alias] -> [Docs.Alias]
240−filterAliases (Var.Listing exports open) aliases =
241− case open of
242− True -> aliases
243− False ->
244− let names =
245− Set.fromList (Var.getAliases exports)
246− in
247− filter (\value -> Set.member (Docs.aliasName value) names) aliases
248−
249−
250−filterUnions :: Var.Listing Var.Value -> [Docs.Union] -> [Docs.Union]
251−filterUnions (Var.Listing exports open) unions =
252− case open of
253− True -> unions
254− False ->
255− Maybe.mapMaybe (filterUnion exportedUnions) unions
256− where
257− exportedUnions =
258− Map.fromList (Var.getUnions exports)
259−
260−
261−filterUnion :: Map.Map String (Var.Listing String) -> Docs.Union -> Maybe Docs.Union
262−filterUnion exportedUnions union =
263− case Map.lookup (Docs.unionName union) exportedUnions of
264− Nothing ->
265− Nothing
266−
267− Just (Var.Listing tags unionOpen)
268− | unionOpen ->
269− Just union
270− | otherwise ->
271− let publicTags =
272− filter (\(tag, _) -> tag `elem` tags) (Docs.unionCases union)
273− in
274− Just (union { Docs.unionCases = publicTags })
275−
src/Docs/AST.hsnew file+44
@@ -0,0 +1,44 @@
1+module Docs.AST where
2+
3+import qualified Data.Map as Map
4+
5+import qualified Elm.Compiler.Type as Type
6+import qualified Reporting.Annotation as A
7+
8+
9+-- FULL DOCUMENTATION
10+
11+data Docs t = Docs
12+ { comment :: String
13+ , aliases :: Map.Map String (A.Located Alias)
14+ , types :: Map.Map String (A.Located Union)
15+ , values :: Map.Map String (A.Located (Value t))
16+ }
17+
18+
19+type Centralized = Docs (Maybe Type.Type)
20+
21+type Checked = Docs Type.Type
22+
23+
24+-- VALUE DOCUMENTATION
25+
26+data Alias = Alias
27+ { aliasComment :: Maybe String
28+ , aliasArgs :: [String]
29+ , aliasType :: Type.Type
30+ }
31+
32+
33+data Union = Union
34+ { unionComment :: Maybe String
35+ , unionArgs :: [String]
36+ , unionCases :: [(String, [Type.Type])]
37+ }
38+
39+
40+data Value t = Value
41+ { valueComment :: Maybe String
42+ , valueType :: t
43+ , valueAssocPrec :: Maybe (String,Int)
44+ }
src/Docs/Centralize.hsnew file+100
@@ -0,0 +1,100 @@
1+module Docs.Centralize (centralize) where
2+
3+import Control.Arrow (second)
4+import qualified Data.Map as Map
5+
6+import qualified AST.Declaration as D
7+import qualified AST.Expression.Canonical as Canonical
8+import qualified AST.Pattern as P
9+import qualified Docs.AST as Docs
10+import qualified Elm.Compiler.Type as Type
11+import qualified Elm.Compiler.Type.Extract as Extract
12+import qualified Reporting.Annotation as A
13+
14+
15+-- CENTRALIZE DOCUMENTATION
16+
17+centralize :: [D.CanonicalDecl] -> String -> Docs.Centralized
18+centralize decls comment =
19+ let
20+ (Raw aliases types valueInfo fixities) =
21+ toRawDocs decls
22+
23+ values =
24+ Map.fromList (map (toValue fixities) (Map.toList valueInfo))
25+ in
26+ Docs.Docs comment aliases types values
27+
28+
29+toValue
30+ :: Map.Map String (String, Int)
31+ -> ( String, A.Located (Maybe String, Maybe Type.Type) )
32+ -> ( String, A.Located (Docs.Value (Maybe Type.Type)) )
33+toValue fixities (name, A.A region (maybeComment, maybeType)) =
34+ ( name
35+ , A.A region (Docs.Value maybeComment maybeType (Map.lookup name fixities))
36+ )
37+
38+
39+-- GATHER INFORMATION
40+
41+data Raw = Raw
42+ { rawAliases :: Map.Map String (A.Located Docs.Alias)
43+ , rawTypes :: Map.Map String (A.Located Docs.Union)
44+ , rawValues :: Map.Map String (A.Located (Maybe String, Maybe Type.Type))
45+ , rawFixities :: Map.Map String (String, Int)
46+ }
47+
48+
49+toRawDocs :: [D.CanonicalDecl] -> Raw
50+toRawDocs decls =
51+ foldr addDeclToDocs (Raw Map.empty Map.empty Map.empty Map.empty) decls
52+
53+
54+addDeclToDocs :: D.CanonicalDecl -> Raw -> Raw
55+addDeclToDocs (A.A (region,maybeComment) decl) docs =
56+ case decl of
57+ D.Definition (Canonical.Definition (A.A subregion (P.Var name)) _ maybeType) ->
58+ let
59+ info =
60+ ( maybeComment
61+ , fmap (Extract.toAliasedType . A.drop) maybeType
62+ )
63+
64+ newValues =
65+ Map.insert name (A.A subregion info) (rawValues docs)
66+ in
67+ docs { rawValues = newValues }
68+
69+ D.Definition _ ->
70+ docs
71+
72+ D.Datatype name args ctors ->
73+ let
74+ ctors' =
75+ map (second (map Extract.toAliasedType)) ctors
76+
77+ union =
78+ A.A region (Docs.Union maybeComment args ctors')
79+ in
80+ docs { rawTypes = Map.insert name union (rawTypes docs) }
81+
82+ D.TypeAlias name args tipe ->
83+ let
84+ alias =
85+ A.A region (Docs.Alias maybeComment args (Extract.toAliasedType tipe))
86+ in
87+ docs { rawAliases = Map.insert name alias (rawAliases docs) }
88+
89+ D.Port _ ->
90+ docs
91+
92+ D.Fixity assoc precedence name ->
93+ let
94+ fixity =
95+ (D.assocToString assoc, precedence)
96+ in
97+ docs
98+ { rawFixities =
99+ Map.insert name fixity (rawFixities docs)
100+ }
src/Docs/Check.hsnew file+180
@@ -0,0 +1,180 @@
1+module Docs.Check (check) where
2+
3+import Control.Applicative ((<$>),(<*>))
4+import qualified Data.List as List
5+import qualified Data.Map as Map
6+import qualified Data.Set as Set
7+import qualified Data.Traversable as T
8+import Text.Parsec ((<|>), choice, many, setPosition, string)
9+import Text.Parsec.Pos (newPos)
10+
11+import qualified AST.Variable as Var
12+import qualified Docs.AST as Docs
13+import qualified Elm.Compiler.Type as Type
14+import Elm.Utils ((|>))
15+import Parse.Helpers
16+ ( addLocation, anyUntil, commaSep1, iParse, parens, simpleNewline, symOp
17+ , var, whitespace
18+ )
19+import qualified Reporting.Annotation as A
20+import qualified Reporting.Error.Docs as Error
21+import qualified Reporting.Error.Helpers as Error (nearbyNames)
22+import qualified Reporting.Region as R
23+import qualified Reporting.Result as R
24+
25+
26+type Result w a = R.Result w Error.Error a
27+
28+
29+-- CHECK DOCUMENTATION
30+
31+check :: [Var.Value] -> A.Located (Maybe Docs.Centralized) -> Result w Docs.Checked
32+check exports (A.A region maybeDocs) =
33+ case maybeDocs of
34+ Nothing ->
35+ R.throw region Error.NoDocs
36+
37+ Just docs ->
38+ checkHelp region exports docs
39+
40+
41+checkHelp :: R.Region -> [Var.Value] -> Docs.Centralized -> Result w Docs.Checked
42+checkHelp region exports (Docs.Docs comment aliases types values) =
43+ let
44+ docNames =
45+ commentedNames region comment
46+
47+ docSet =
48+ Set.fromList (map A.drop docNames)
49+
50+ checkCategory checkValue dict =
51+ dict
52+ |> Map.filterWithKey (\key _ -> Set.member key docSet)
53+ |> Map.mapWithKey checkValue
54+ |> T.traverse id
55+
56+ exportedUnions =
57+ map (\(tipe, Var.Listing tags _) -> (tipe, tags)) (Var.getUnions exports)
58+ in
59+ (\() -> Docs.Docs comment)
60+ <$> checkModuleComment region exports docNames
61+ <*> checkCategory (checkComment Docs.aliasComment) aliases
62+ <*> checkCategory (checkUnion exportedUnions) types
63+ <*> checkCategory checkValue values
64+
65+
66+checkComment :: (a -> Maybe String) -> String -> A.Located a -> Result w (A.Located a)
67+checkComment getComment name (A.A region value) =
68+ case getComment value of
69+ Nothing ->
70+ R.throw region (Error.NoComment name)
71+
72+ Just _ ->
73+ R.ok (A.A region value)
74+
75+
76+checkUnion :: [(String, [String])] -> String -> A.Located Docs.Union -> Result w (A.Located Docs.Union)
77+checkUnion exportedUnions name value =
78+ const
79+ <$> filterUnionTags exportedUnions name value
80+ <*> checkComment Docs.unionComment name value
81+
82+
83+filterUnionTags :: [(String, [String])] -> String -> A.Located Docs.Union -> Result w (A.Located Docs.Union)
84+filterUnionTags exportedUnions name (A.A region union@(Docs.Union _ _ ctors)) =
85+ let
86+ exportedTags =
87+ maybe [] id (List.lookup name exportedUnions)
88+ in
89+ R.ok $ A.A region $ union
90+ { Docs.unionCases =
91+ filter (\(tag, _) -> elem tag exportedTags) ctors
92+ }
93+
94+
95+
96+checkValue :: String -> A.Located (Docs.Value (Maybe Type.Type)) -> Result w (A.Located (Docs.Value Type.Type))
97+checkValue name value =
98+ const
99+ <$> hasType name value
100+ <*> checkComment Docs.valueComment name value
101+
102+
103+hasType :: String -> A.Located (Docs.Value (Maybe Type.Type)) -> Result w (A.Located (Docs.Value Type.Type))
104+hasType name (A.A region value) =
105+ case Docs.valueType value of
106+ Just tipe ->
107+ R.ok (A.A region (value { Docs.valueType = tipe }))
108+
109+ Nothing ->
110+ R.throw region (Error.NoType name)
111+
112+
113+-- CHECK MODULE COMMENT
114+
115+checkModuleComment :: R.Region -> [Var.Value] -> [A.Located String] -> Result w ()
116+checkModuleComment docRegion exports locatedDocNames =
117+ let
118+ exportNames =
119+ map valueName exports
120+
121+ exportDict =
122+ Map.fromList (map (\name -> (name, ())) exportNames)
123+
124+ docDict =
125+ Map.fromList (map (\(A.A region name) -> (name, region)) locatedDocNames)
126+
127+ extraNameError (name, region) =
128+ R.throw region (Error.OnlyInDocs name (Error.nearbyNames id name exportNames))
129+
130+ docErrors =
131+ Map.difference docDict exportDict
132+ |> Map.toList
133+ |> T.traverse extraNameError
134+
135+ namesOnlyInExports =
136+ Map.difference exportDict docDict
137+ |> Map.toList
138+ |> map fst
139+
140+ exportErrors =
141+ if null namesOnlyInExports then
142+ R.ok ()
143+ else
144+ R.throw docRegion (Error.OnlyInExports namesOnlyInExports)
145+ in
146+ (\_ _ -> ())
147+ <$> exportErrors
148+ <*> docErrors
149+
150+
151+commentedNames :: R.Region -> String -> [A.Located String]
152+commentedNames (R.Region start _) comment =
153+ let
154+ nameParser =
155+ choice
156+ [ do string "@docs"
157+ whitespace
158+ commaSep1 (addLocation (var <|> parens symOp))
159+ , do anyUntil simpleNewline
160+ return []
161+ ]
162+
163+ docCommentParser =
164+ do setPosition (newPos "docs" (R.line start) (R.column start + 3))
165+ concat <$> many nameParser
166+ in
167+ case iParse docCommentParser comment of
168+ Left _ ->
169+ []
170+
171+ Right names ->
172+ names
173+
174+
175+valueName :: Var.Value -> String
176+valueName value =
177+ case value of
178+ Var.Value name -> name
179+ Var.Alias name -> name
180+ Var.Union name _ -> name
src/Elm/Compiler.hs+126−37
@@ -1,24 +1,29 @@
11{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE FlexibleContexts #-}
32module Elm.Compiler
43 ( version, rawVersion
5− , parseDependencies, compile
6− , Error, errorToString
7− , Warning
4+ , parseDependencies
5+ , compile, Context(..), Result(..)
6+ , Dealiaser, dummyDealiaser
7+ , Error, errorToString, errorToJson, printError
8+ , Warning, warningToString, warningToJson, printWarning
89 ) where
910
10−import Control.Monad.Error.Class (MonadError, throwError)
11+import qualified Data.Aeson as Json
1112import qualified Data.Map as Map
1213
1314import qualified AST.Module as Module
1415import qualified Compile
16+import qualified Docs.Check as Docs
1517import qualified Elm.Compiler.Module as PublicModule
1618import qualified Elm.Compiler.Version as Version
19+import qualified Elm.Docs as Docs
1720import qualified Generate.JavaScript as JS
18−import qualified Parse.Helpers as Help
21+import qualified Optimize
1922import qualified Parse.Module as Parse
23+import qualified Parse.Parse as Parse
2024import qualified Reporting.Annotation as A
2125import qualified Reporting.Error as Error
26+import qualified Reporting.PrettyPrint as P
2227import qualified Reporting.Result as Result
2328import qualified Reporting.Warning as Warning
2429
@@ -38,56 +43,140 @@ rawVersion =
3843-- DEPENDENCIES
3944
4045parseDependencies
41− :: (MonadError String m)
42− => String
43− -> m (PublicModule.Name, [PublicModule.Name])
44−parseDependencies src =
45− case Help.iParse Parse.header src of
46− Left msg ->
47− throwError (show msg)
46+ :: String
47+ -> Either [Error] (PublicModule.Name, [PublicModule.Name])
48+parseDependencies sourceCode =
49+ let
50+ (Result.Result _warnings rawResult) =
51+ Parse.parse sourceCode Parse.header
52+ in
53+ case rawResult of
54+ Result.Err msgs ->
55+ Left $ map (Error . A.map Error.Syntax) msgs
4856
49− Right (Module.Header names _exports imports) ->
50− return
51− ( PublicModule.Name names
52− , map (PublicModule.Name . fst . A.drop) imports
53− )
57+ Result.Ok (Module.Header names _docs _exports imports) ->
58+ Right
59+ ( PublicModule.Name names
60+ , map (PublicModule.Name . fst . A.drop) imports
61+ )
5462
5563
5664-- COMPILATION
5765
5866{-| Compiles Elm source code to JavaScript. -}
5967compile
60− :: String
61− -> String
68+ :: Context
6269 -> String
6370 -> Map.Map PublicModule.Name PublicModule.Interface
64− -> ([Warning], Either [Error] (PublicModule.Interface, String))
65−compile user packageName source interfaces =
66− let unwrappedInterfaces =
67− Map.mapKeysMonotonic (\(PublicModule.Name name) -> name) interfaces
71+ -> (Dealiaser, [Warning], Either [Error] Result)
72+
73+compile context source interfaces =
74+ let
75+ (Context user packageName isRoot isExposed) =
76+ context
77+
78+ unwrappedInterfaces =
79+ Map.mapKeysMonotonic (\(PublicModule.Name name) -> name) interfaces
80+
81+ (Result.Result (dealiaser, warnings) rawResult) =
82+ do modul <- Compile.compile user packageName isRoot unwrappedInterfaces source
83+ docs <- docsGen isExposed modul
84+
85+ let interface = Module.toInterface modul
86+ let optModule = Optimize.optimize modul
87+ let javascript = JS.generate optModule
6888
69− (Result.Result warnings rawResult) =
70− Compile.compile user packageName unwrappedInterfaces source
89+ return (Result docs interface javascript)
7190 in
72− (,) (map Warning warnings) $
73− case rawResult of
74− Result.Ok modul ->
75− Right (Module.toInterface modul, JS.generate modul)
91+ ( maybe dummyDealiaser Dealiaser dealiaser
92+ , map Warning warnings
93+ , Result.destruct (Left . map Error) Right rawResult
94+ )
95+
96+
97+data Context = Context
98+ { _user :: String
99+ , _packageName :: String
100+ , _isRoot :: Bool
101+ , _isExposed :: Bool
102+ }
103+
104+
105+data Result = Result
106+ { _docs :: Maybe Docs.Documentation
107+ , _interface :: PublicModule.Interface
108+ , _js :: String
109+ }
110+
111+
112+docsGen
113+ :: Bool
114+ -> Module.CanonicalModule
115+ -> Result.Result w Error.Error (Maybe Docs.Documentation)
116+docsGen isExposed modul =
117+ if not isExposed then
118+ Result.ok Nothing
119+ else
120+ let
121+ getChecked =
122+ Docs.check (Module.exports modul) (Module.docs modul)
123+
124+ toDocs checked =
125+ Docs.fromCheckedDocs (PublicModule.Name (Module.names modul)) checked
126+ in
127+ (Just . toDocs) `fmap` Result.mapError Error.Docs getChecked
128+
76129
77− Result.Err errors ->
78− Left (map Error errors)
130+-- DEALIASER
131+
132+newtype Dealiaser =
133+ Dealiaser P.Dealiaser
134+
135+
136+dummyDealiaser :: Dealiaser
137+dummyDealiaser =
138+ Dealiaser Map.empty
79139
80140
81141-- ERRORS
82142
83−newtype Error = Error (A.Located Error.Error)
143+newtype Error =
144+ Error (A.Located Error.Error)
145+
84146
147+errorToString :: Dealiaser -> String -> String -> Error -> String
148+errorToString (Dealiaser dealiaser) location source (Error err) =
149+ Error.toString dealiaser location source err
85150
86−errorToString :: String -> String -> Error -> String
87−errorToString location source (Error err) =
88− Error.toString location source err
151+
152+printError :: Dealiaser -> String -> String -> Error -> IO ()
153+printError (Dealiaser dealiaser) location source (Error err) =
154+ Error.print dealiaser location source err
155+
156+
157+errorToJson :: Dealiaser -> String -> Error -> Json.Value
158+errorToJson (Dealiaser dealiaser) location (Error err) =
159+ Error.toJson dealiaser location err
89160
90161
91162-- WARNINGS
92163
93−newtype Warning = Warning (A.Located Warning.Warning)
164+newtype Warning =
165+ Warning (A.Located Warning.Warning)
166+
167+
168+warningToString :: Dealiaser -> String -> String -> Warning -> String
169+warningToString (Dealiaser dealiaser) location source (Warning err) =
170+ Warning.toString dealiaser location source err
171+
172+
173+printWarning :: Dealiaser -> String -> String -> Warning -> IO ()
174+printWarning (Dealiaser dealiaser) location source (Warning err) =
175+ Warning.print dealiaser location source err
176+
177+
178+warningToJson :: Dealiaser -> String -> Warning -> Json.Value
179+warningToJson (Dealiaser dealiaser) location (Warning err) =
180+ Warning.toJson dealiaser location err
181+
182+
src/Elm/Compiler/Module.hs+4−4
@@ -5,7 +5,7 @@ module Elm.Compiler.Module
55 , hyphenate, dehyphenate
66 , defaultImports
77 , interfacePorts
8− , interfaceTypes
8+ , interfaceAliasedTypes
99 )
1010 where
1111
@@ -44,9 +44,9 @@ interfacePorts interface =
4444 Module.iPorts interface
4545
4646
47−interfaceTypes :: Interface -> Map.Map String Type.Type
48−interfaceTypes interface =
49− Map.map Extract.fromInternalType (Module.iTypes interface)
47+interfaceAliasedTypes :: Interface -> Map.Map String Type.Type
48+interfaceAliasedTypes interface =
49+ Map.map Extract.toAliasedType (Module.iTypes interface)
5050
5151
5252-- STRING CONVERSIONS for NAMES
src/Elm/Compiler/Type.hs+62−49
@@ -6,13 +6,19 @@ module Elm.Compiler.Type
66
77import Control.Applicative ((<$>), (<*>))
88import Control.Arrow (second)
9−import Data.Aeson ((.:), (.=))
9+import Data.Aeson ((.:))
1010import qualified Data.Aeson as Json
11+import qualified Data.Aeson.Types as Json
1112import qualified Data.ByteString.Lazy.Char8 as BS
1213import qualified Data.Text as Text
1314import Text.PrettyPrint as P
1415
1516import qualified AST.Helpers as Help
17+import qualified AST.Type as Type
18+import qualified AST.Variable as Var
19+import qualified Parse.Helpers as Parse
20+import qualified Parse.Type as Type
21+import qualified Reporting.Annotation as A
1622
1723
1824data Type
@@ -88,9 +94,10 @@ toDoc context tipe =
8894 (P.lbrace <+> P.text x <+> P.text "|")
8995 4
9096 (P.sep
91− [ P.cat (zipWith (<+>) (P.space : repeat P.comma) (map prettyField fields))
97+ [ P.sep (P.punctuate P.comma (map prettyField fields))
9298 , P.rbrace
93− ])
99+ ]
100+ )
94101 where
95102 prettyField (field, tipe) =
96103 P.text field <+> P.text ":" <+> toDoc None tipe
@@ -121,62 +128,68 @@ flattenRecord tipe =
121128-- JSON for TYPE
122129
123130instance Json.ToJSON Type where
124− toJSON tipe =
125− Json.object (getFields tipe)
126− where
127− getFields tipe =
128− case tipe of
129− Lambda t1 t2 ->
130− [ "tag" .= ("lambda" :: Text.Text)
131− , "in" .= Json.toJSON t1
132− , "out" .= Json.toJSON t2
133− ]
131+ toJSON tipe =
132+ Json.String (Text.unwords (Text.words (Text.pack (toString tipe))))
134133
135− Var x ->
136− [ "tag" .= ("var" :: Text.Text)
137− , "name" .= Json.toJSON x
138− ]
139134
140− Type name ->
141− [ "tag" .= ("type" :: Text.Text)
142− , "name" .= Json.toJSON name
143− ]
135+instance Json.FromJSON Type where
136+ parseJSON value =
137+ let
138+ failure _ =
139+ fail $
140+ "Trying to decode a type string, but could not handle this value:\n"
141+ ++ BS.unpack (Json.encode value)
142+ in
143+ case value of
144+ Json.String text ->
145+ either failure (return . fromRawType)
146+ (Parse.iParse Type.expr (Text.unpack text))
144147
145− App t ts ->
146− [ "tag" .= ("app" :: Text.Text)
147− , "func" .= Json.toJSON t
148− , "args" .= Json.toJSON ts
149− ]
148+ Json.Object obj ->
149+ fromObject obj
150150
151− Record fields ext ->
152− [ "tag" .= ("record" :: Text.Text)
153− , "fields" .= Json.toJSON (map (Json.toJSON . second Json.toJSON) fields)
154− , "extension" .= Json.toJSON ext
155− ]
151+ _ ->
152+ failure ()
156153
157154
158−instance Json.FromJSON Type where
159− parseJSON (Json.Object obj) =
160− do tag <- obj .: "tag"
161− case (tag :: String) of
162− "lambda" ->
163− Lambda <$> obj .: "in" <*> obj .: "out"
155+fromObject :: Json.Object -> Json.Parser Type
156+fromObject obj =
157+ do tag <- obj .: "tag"
158+ case (tag :: String) of
159+ "lambda" ->
160+ Lambda <$> obj .: "in" <*> obj .: "out"
161+
162+ "var" ->
163+ Var <$> obj .: "name"
164+
165+ "type" ->
166+ Type <$> obj .: "name"
167+
168+ "app" ->
169+ App <$> obj .: "func" <*> obj .: "args"
170+
171+ "record" ->
172+ Record <$> obj .: "fields" <*> obj .: "extension"
173+
174+ _ ->
175+ fail $ "Error when decoding type with tag: " ++ tag
164176
165− "var" ->
166− Var <$> obj .: "name"
167177
168− "type" ->
169− Type <$> obj .: "name"
178+fromRawType :: Type.Raw -> Type
179+fromRawType (A.A _ astType) =
180+ case astType of
181+ Type.RLambda t1 t2 ->
182+ Lambda (fromRawType t1) (fromRawType t2)
170183
171− "app" ->
172− App <$> obj .: "func" <*> obj .: "args"
184+ Type.RVar x ->
185+ Var x
173186
174− "record" ->
175− Record <$> obj .: "fields" <*> obj .: "extension"
187+ Type.RType var ->
188+ Type (Var.toString var)
176189
177− _ ->
178− fail $ "Error when decoding type with tag: " ++ tag
190+ Type.RApp t ts ->
191+ App (fromRawType t) (map fromRawType ts)
179192
193+ Type.RRecord fields ext ->
194+ Record (map (second fromRawType) fields) (fmap fromRawType ext)
180195
181− parseJSON value =
182− fail $ "Cannot decode Value from: " ++ BS.unpack (Json.encode value)
src/Elm/Compiler/Type/Extract.hs+23−9
@@ -1,4 +1,4 @@
1−module Elm.Compiler.Type.Extract where
1+module Elm.Compiler.Type.Extract (toFullType, toAliasedType) where
22
33import Control.Arrow (second)
44import qualified AST.Type as Type
@@ -6,13 +6,24 @@ import qualified AST.Variable as Var
66import qualified Elm.Compiler.Type as T
77
88
9−-- INTERNAL TYPE to PUBLIC TYPE
9+toFullType :: Type.Canonical -> T.Type
10+toFullType astType =
11+ toType False astType
1012
11−fromInternalType :: Type.Canonical -> T.Type
12−fromInternalType astType =
13+
14+toAliasedType :: Type.Canonical -> T.Type
15+toAliasedType astType =
16+ toType True astType
17+
18+
19+toType :: Bool -> Type.Canonical -> T.Type
20+toType useAliases astType =
21+ let
22+ go = toType useAliases
23+ in
1324 case astType of
1425 Type.Lambda t1 t2 ->
15− T.Lambda (fromInternalType t1) (fromInternalType t2)
26+ T.Lambda (go t1) (go t2)
1627
1728 Type.Var x ->
1829 T.Var x
@@ -21,10 +32,13 @@ fromInternalType astType =
2132 T.Type (Var.toString var)
2233
2334 Type.App t ts ->
24− T.App (fromInternalType t) (map fromInternalType ts)
35+ T.App (go t) (map go ts)
2536
2637 Type.Record fields ext ->
27− T.Record (map (second fromInternalType) fields) (fmap fromInternalType ext)
38+ T.Record (map (second go) fields) (fmap go ext)
2839
29− Type.Aliased _name args t ->
30− fromInternalType (Type.dealias args t)
40+ Type.Aliased name args t ->
41+ if useAliases then
42+ T.App (T.Type (Var.toString name)) (map (go . snd) args)
43+ else
44+ go (Type.dealias args t)
src/Elm/Docs.hs+82−3
@@ -1,14 +1,20 @@
11{-# LANGUAGE OverloadedStrings #-}
22module Elm.Docs where
33
4−import Control.Applicative ((<$>),(<*>))
4+import Control.Applicative ((<$>),(<*>),(<|>))
55import Control.Monad
66import Data.Aeson ((.:), (.:?), (.=))
77import qualified Data.Aeson as Json
8+import qualified Data.Aeson.Encode.Pretty as Json
89import qualified Data.ByteString.Lazy.Char8 as BS
10+import qualified Data.Map as Map
11+import qualified Data.Text as Text
912
13+import qualified Docs.AST as Docs
1014import qualified Elm.Compiler.Module as Module
1115import qualified Elm.Compiler.Type as Type
16+import qualified Elm.Compiler.Version as Version
17+import qualified Reporting.Annotation as A
1218
1319
1420data Documentation = Documentation
@@ -17,6 +23,7 @@ data Documentation = Documentation
1723 , aliases :: [Alias]
1824 , types :: [Union]
1925 , values :: [Value]
26+ , version :: Version
2027 }
2128
2229
@@ -40,22 +47,71 @@ data Value = Value
4047 { valueName :: String
4148 , valueComment :: String
4249 , valueType :: Type.Type
43− , valueAssocPrec :: Maybe (String,Int)
50+ , valueFix :: Maybe (String,Int)
4451 }
4552
4653
54+data Version = NonCanonicalTypes | Version String
55+
56+
57+-- FROM CHECKED DOCS
58+
59+fromCheckedDocs :: Module.Name -> Docs.Checked -> Documentation
60+fromCheckedDocs name (Docs.Docs comment aliases unions values) =
61+ let
62+ unwrap cmnt =
63+ maybe "" id cmnt
64+
65+ toAlias (name, (A.A _ (Docs.Alias cmnt args tipe))) =
66+ Alias name (unwrap cmnt) args tipe
67+
68+ toUnion (name, (A.A _ (Docs.Union cmnt args cases))) =
69+ Union name (unwrap cmnt) args cases
70+
71+ toValue (name, (A.A _ (Docs.Value cmnt tipe fix))) =
72+ Value name (unwrap cmnt) tipe fix
73+ in
74+ Documentation name comment
75+ (map toAlias (Map.toList aliases))
76+ (map toUnion (Map.toList unions))
77+ (map toValue (Map.toList values))
78+ (Version Version.version)
79+
80+
81+-- PRETTY JSON
82+
83+prettyJson :: (Json.ToJSON a) => a -> BS.ByteString
84+prettyJson value =
85+ Json.encodePretty' config value
86+
87+
88+config :: Json.Config
89+config =
90+ Json.Config
91+ { Json.confIndent = 2
92+ , Json.confCompare = Json.keyOrder keys
93+ }
94+ where
95+ keys =
96+ [ "tag", "name", "comment", "aliases", "types"
97+ , "values", "func", "args", "type", "cases"
98+ ]
99+
100+
47101-- JSON for DOCUMENTATION
48102
49103instance Json.ToJSON Documentation where
50− toJSON (Documentation name comment aliases types values) =
104+ toJSON (Documentation name comment aliases types values version) =
51105 Json.object
52106 [ "name" .= name
53107 , "comment" .= comment
54108 , "aliases" .= aliases
55109 , "types" .= types
56110 , "values" .= values
111+ , "generated-with-elm-version" .= version
57112 ]
58113
114+
59115instance Json.FromJSON Documentation where
60116 parseJSON (Json.Object obj) =
61117 Documentation
@@ -64,11 +120,34 @@ instance Json.FromJSON Documentation where
64120 <*> obj .: "aliases"
65121 <*> obj .: "types"
66122 <*> obj .: "values"
123+ <*> ((obj .: "generated-with-elm-version") <|> return NonCanonicalTypes)
67124
68125 parseJSON value =
69126 fail $ "Cannot decode Documentation from: " ++ BS.unpack (Json.encode value)
70127
71128
129+-- JSON for VERSION
130+
131+instance Json.ToJSON Version where
132+ toJSON version =
133+ case version of
134+ NonCanonicalTypes ->
135+ Json.String "old"
136+
137+ Version vsn ->
138+ Json.String (Text.pack vsn)
139+
140+
141+instance Json.FromJSON Version where
142+ parseJSON (Json.String text) =
143+ case Text.unpack text of
144+ "old" -> return NonCanonicalTypes
145+ vrsn -> return (Version vrsn)
146+
147+ parseJSON value =
148+ fail $ "Cannot decode version of documentation from: " ++ BS.unpack (Json.encode value)
149+
150+
72151-- JSON for ALIAS
73152
74153instance Json.ToJSON Alias where
src/Elm/Utils.hs+30−3
@@ -1,8 +1,15 @@
11{-# OPTIONS_GHC -Wall #-}
22{-# LANGUAGE FlexibleContexts #-}
3−module Elm.Utils ((|>), (<|), getAsset, run, unwrappedRun, CommandError(..)) where
3+module Elm.Utils
4+ ( (|>), (<|)
5+ , getAsset
6+ , run, unwrappedRun
7+ , CommandError(..)
8+ , isDeclaration
9+ ) where
410
511import Control.Monad.Except (MonadError, MonadIO, liftIO, throwError)
12+import qualified Data.List as List
613import System.Directory (doesFileExist)
714import System.Environment (getEnv)
815import System.Exit (ExitCode(ExitSuccess, ExitFailure))
@@ -10,7 +17,12 @@ import System.FilePath ((</>))
1017import System.IO.Error (tryIOError)
1118import System.Process (readProcessWithExitCode)
1219
20+import qualified AST.Expression.Source as Source
21+import qualified AST.Pattern as Pattern
1322import qualified Elm.Compiler.Version as Version
23+import qualified Parse.Helpers as Parse
24+import qualified Parse.Expression as Parse
25+import qualified Reporting.Annotation as A
1426
1527
1628{-| Forward function application `x |> f == f x`. This function is useful
@@ -75,7 +87,7 @@ unwrappedRun command args =
7587missingExe :: String -> CommandError
7688missingExe command =
7789 MissingExe $
78− "Could not find command '" ++ command ++ "'. Do you have it installed?\n\
90+ "Could not find command `" ++ command ++ "`. Do you have it installed?\n\
7991 \ Can it be run from anywhere? Is it on your PATH?"
8092
8193
@@ -116,6 +128,21 @@ errorNotFound name =
116128 , " C:/Program Files/Elm Platform/" ++ Version.version ++ "/share"
117129 , " C:/Program Files (x86)/Elm Platform/" ++ Version.version ++ "/share"
118130 , ""
131+ , "If you installed using npm, you have to set ELM_HOME to a certain directory"
132+ , "of the form .../node_modules/elm/share"
133+ , ""
119134 , "If it seems like a more complex issue, please report it here:"
120135 , " <https://github.com/elm-lang/elm-platform/issues>"
121− ]
136+ ]
137+
138+
139+-- DECL CHECKER
140+
141+isDeclaration :: String -> Maybe String
142+isDeclaration string =
143+ case Parse.iParse Parse.definition string of
144+ Right (A.A _ (Source.Definition pattern _)) ->
145+ Just (List.intercalate "$" (Pattern.boundVarList pattern))
146+
147+ _ ->
148+ Nothing
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 def@(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 :: 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/Generate/JavaScript/Port.hs+4−3
@@ -1,6 +1,7 @@
11module Generate.JavaScript.Port (inbound, outbound, task) where
22
33import qualified Data.List as List
4+import qualified Data.Map as Map
45import Language.ECMAScript3.Syntax
56
67import qualified AST.Type as T
@@ -40,7 +41,7 @@ typeToString tipe =
4041 JSString -> "a string"
4142 JSArray -> "an array"
4243 JSObject fields ->
43− "an object with fields '" ++ List.intercalate "', '" fields ++ "'"
44+ "an object with fields `" ++ List.intercalate "`, `" fields ++ "`"
4445
4546
4647_Array :: String -> Expression ()
@@ -96,14 +97,14 @@ inbound name portType =
9697 T.Normal tipe ->
9798 _Port "inbound" `call`
9899 [ string name
99− , string (show (P.pretty False tipe))
100+ , string (show (P.pretty Map.empty False tipe))
100101 , toTypeFunction tipe
101102 ]
102103
103104 T.Signal _root arg ->
104105 _Port "inboundSignal" `call`
105106 [ string name
106− , string (show (P.pretty False arg))
107+ , string (show (P.pretty Map.empty False arg))
107108 , toTypeFunction arg
108109 ]
109110
src/Nitpick/TopLevelTypes.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Nitpick.TopLevelTypes (topLevelTypes) where
3+
4+import Prelude hiding (maybe)
5+import qualified Data.Foldable as F
6+import qualified Data.Map as Map
7+
8+import qualified AST.Expression.Valid as Valid
9+import qualified AST.Declaration as Decl
10+import qualified AST.Pattern as P
11+import qualified AST.Type as Type
12+import qualified AST.Variable as Var
13+import qualified Reporting.Annotation as A
14+import qualified Reporting.Error.Type as Error
15+import qualified Reporting.Result as Result
16+import qualified Reporting.Warning as Warning
17+
18+
19+topLevelTypes
20+ :: Map.Map String Type.Canonical
21+ -> [Decl.ValidDecl]
22+ -> Result.Result Warning.Warning Error.Error ()
23+topLevelTypes typeEnv validDecls =
24+ do F.traverse_ (warnMissingAnnotation typeEnv) validDecls
25+ checkMainType typeEnv validDecls
26+
27+
28+-- MISSING ANNOTATIONS
29+
30+warnMissingAnnotation
31+ :: Map.Map String Type.Canonical
32+ -> Decl.ValidDecl
33+ -> Result.Result Warning.Warning Error.Error ()
34+warnMissingAnnotation typeEnv (A.A (region,_) decl) =
35+ case decl of
36+ Decl.Definition (Valid.Definition (A.A _ (P.Var name)) _ Nothing) ->
37+ case Map.lookup name typeEnv of
38+ Nothing ->
39+ return ()
40+ Just tipe ->
41+ Result.warn region (Warning.MissingTypeAnnotation name tipe)
42+ _ ->
43+ return ()
44+
45+
46+-- MAIN TYPE
47+
48+checkMainType
49+ :: Map.Map String Type.Canonical
50+ -> [Decl.ValidDecl]
51+ -> Result.Result w Error.Error ()
52+checkMainType typeEnv decls =
53+ case decls of
54+ A.A (region,_) (Decl.Definition (Valid.Definition (A.A _ (P.Var "main")) _ _)) : _ ->
55+ case Map.lookup "main" typeEnv of
56+ Nothing ->
57+ return ()
58+
59+ Just typeOfMain ->
60+ let tipe = Type.deepDealias typeOfMain
61+ in
62+ if tipe `elem` validMainTypes
63+ then return ()
64+ else Result.throw region (Error.BadMain typeOfMain)
65+
66+ _ : remainingDecls ->
67+ checkMainType typeEnv remainingDecls
68+
69+ [] ->
70+ return ()
71+
72+
73+validMainTypes :: [Type.Canonical]
74+validMainTypes =
75+ [ element
76+ , html
77+ , signal element
78+ , signal html
79+ ]
80+ where
81+ fromModule :: [String] -> String -> Type.Canonical
82+ fromModule home name =
83+ Type.Type (Var.fromModule home name)
84+
85+ html =
86+ fromModule ["VirtualDom"] "Node"
87+
88+ signal tipe =
89+ Type.App (fromModule ["Signal"] "Signal") [ tipe ]
90+
91+ element =
92+ let builtin name =
93+ Type.Type (Var.builtin name)
94+
95+ maybe tipe =
96+ Type.App (fromModule ["Maybe"] "Maybe") [ tipe ]
97+ in
98+ Type.Record
99+ [ ("element", fromModule ["Graphics","Element"] "ElementPrim")
100+ , ("props",
101+ Type.Record
102+ [ ("click" , builtin "_Tuple0")
103+ , ("color" , maybe (fromModule ["Color"] "Color"))
104+ , ("height" , builtin "Int")
105+ , ("hover" , builtin "_Tuple0")
106+ , ("href" , builtin "String")
107+ , ("id" , builtin "Int")
108+ , ("opacity", builtin "Float")
109+ , ("tag" , builtin "String")
110+ , ("width" , builtin "Int")
111+ ]
112+ Nothing
113+ )
114+ ]
115+ Nothing
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+16−12
@@ -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)
@@ -258,5 +262,5 @@ matchMix vs branches def =
258262
259263noMatch :: String -> a
260264noMatch name =
261− error $ "unexpected pattern in '" ++ name ++
262− "' function. Report a compiler issue to <https://github.com/elm-lang/Elm>."
265+ error $ "unexpected pattern in `" ++ name ++
266+ "` function. Report a compiler issue to <https://github.com/elm-lang/Elm>."
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/Declaration.hs+11−4
@@ -6,27 +6,32 @@ import Text.Parsec ( (<|>), (<?>), choice, digit, optionMaybe, string, try )
66
77import qualified AST.Declaration as D
88import qualified Parse.Expression as Expr
9−import Parse.Helpers
9+import Parse.Helpers as Help
1010import qualified Parse.Type as Type
1111
1212
1313declaration :: IParser D.SourceDecl
1414declaration =
15− addLocation (typeDecl <|> infixDecl <|> port <|> definition)
15+ choice
16+ [ D.Comment <$> Help.docComment
17+ , D.Decl <$> addLocation (typeDecl <|> infixDecl <|> port <|> definition)
18+ ]
1619
1720
1821-- TYPE ANNOTATIONS and DEFINITIONS
1922
2023definition :: IParser D.SourceDecl'
2124definition =
22− D.Definition <$> (Expr.typeAnnotation <|> Expr.definition)
25+ D.Definition
26+ <$> (Expr.typeAnnotation <|> Expr.definition)
27+ <?> "a value definition"
2328
2429
2530-- TYPE ALIAS and UNION TYPES
2631
2732typeDecl :: IParser D.SourceDecl'
2833typeDecl =
29− do try (reserved "type") <?> "type declaration"
34+ do try (reserved "type") <?> "a type declaration"
3035 forcedWS
3136 isAlias <- optionMaybe (string "alias" >> forcedWS)
3237
@@ -48,6 +53,7 @@ typeDecl =
4853
4954infixDecl :: IParser D.SourceDecl'
5055infixDecl =
56+ expecting "an infix declaration" $
5157 do assoc <-
5258 choice
5359 [ try (reserved "infixl") >> return D.L
@@ -64,6 +70,7 @@ infixDecl =
6470
6571port :: IParser D.SourceDecl'
6672port =
73+ expecting "a port declaration" $
6774 do try (reserved "port")
6875 whitespace
6976 name <- lowVar
src/Parse/Expression.hs+84−50
@@ -18,13 +18,15 @@ 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 --------
2426
2527varTerm :: IParser Source.Expr'
2628varTerm =
27− toVar <$> var <?> "variable"
29+ toVar <$> var
2830
2931
3032toVar :: String -> Source.Expr'
@@ -56,7 +58,10 @@ accessor =
5658negative :: IParser Source.Expr'
5759negative =
5860 do (start, nTerm, end) <-
59− located (try (char '-' >> notFollowedBy (char '.' <|> char '-')) >> term)
61+ located $ try $
62+ do char '-'
63+ notFollowedBy (char '.' <|> char '-')
64+ term
6065
6166 let ann e =
6267 A.at start end e
@@ -132,8 +137,11 @@ parensTerm =
132137recordTerm :: IParser Source.Expr
133138recordTerm =
134139 addLocation $
135− brackets $ choice [ misc, record ]
140+ brackets (choice [ misc, record ])
136141 where
142+ record =
143+ E.Record <$> commaSep field
144+
137145 field =
138146 do label <- rLabel
139147 patterns <- spacePrefix Pattern.term
@@ -141,51 +149,66 @@ recordTerm =
141149 body <- expr
142150 return (label, makeFunction patterns body)
143151
144− record =
145− E.Record <$> commaSep field
146−
147− change =
148− do lbl <- rLabel
149− padded (string "<-")
150− (,) lbl <$> expr
151−
152− remove r =
153− do string "-"
152+ misc =
153+ do nextParser <- try miscStarter
154154 whitespace
155− E.Remove r <$> rLabel
155+ nextParser
156156
157− insert r =
158− do string "|"
157+ miscStarter =
158+ do start <- getMyPosition
159+ record <- addLocation (E.rawVar <$> rLabel)
159160 whitespace
160− E.Insert r <$> rLabel <*> (padded equals >> expr)
161+ choice
162+ [ string "-" >> return (afterMinus start record)
163+ , string "|" >> return (afterBar record)
164+ ]
161165
162− modify r =
163− do string "|"
166+ afterMinus start record =
167+ do field <- rLabel
168+ end <- getMyPosition
169+ let subRecord' = E.Remove record field
170+ maybe <- optionMaybe (try (whitespace >> string "|"))
171+ case maybe of
172+ Nothing ->
173+ return subRecord'
174+ Just _ ->
175+ do whitespace
176+ field <- rLabel
177+ padded equals
178+ E.Insert (A.at start end subRecord') field <$> expr
179+
180+ afterBar record =
181+ do field <- rLabel
164182 whitespace
165− E.Modify r <$> commaSep1 change
183+ choice
184+ [ do equals
185+ whitespace
186+ E.Insert record field <$> expr
187+ , do leftArrow
188+ whitespace
189+ value <- expr
190+ updates <- spaceyPrefixBy comma update
191+ return (E.Modify record ((field,value) : updates))
192+ ]
166193
167− misc =
168− try $ do
169− record <- addLocation (E.rawVar <$> rLabel)
170− opt <- padded (optionMaybe (addLocation (remove record)))
171− case opt of
172− Just e@(A.A _ e') ->
173− try (insert e) <|> return e'
174− Nothing ->
175− try (insert record) <|> try (modify record)
194+ update =
195+ do lbl <- rLabel
196+ padded leftArrow
197+ (,) lbl <$> expr
176198
177199
178200term :: IParser Source.Expr
179201term =
180202 addLocation (choice [ E.Literal <$> Literal.literal, listTerm, accessor, negative ])
181203 <|> accessible (addLocation varTerm <|> parensTerm <|> recordTerm)
182− <?> "basic term (4, x, 'c', etc.)"
204+ <?> "an expression"
183205
184206
185207-------- Applications --------
186208
187209appExpr :: IParser Source.Expr
188210appExpr =
211+ expecting "an expression" $
189212 do t <- term
190213 ts <- constrainedSpacePrefix term $ \str ->
191214 if null str then notFollowedBy (char '-') else return ()
@@ -199,7 +222,8 @@ appExpr =
199222
200223expr :: IParser Source.Expr
201224expr =
202− addLocation (choice [ ifExpr, letExpr, caseExpr ])
225+ addLocation (choice [ letExpr, caseExpr ])
226+ <|> ifExpr
203227 <|> lambdaExpr
204228 <|> binaryExpr
205229 <?> "an expression"
@@ -209,44 +233,54 @@ binaryExpr :: IParser Source.Expr
209233binaryExpr =
210234 Binop.binops appExpr lastExpr anyOp
211235 where
212− lastExpr = addLocation (choice [ ifExpr, letExpr, caseExpr ])
213− <|> lambdaExpr
236+ lastExpr =
237+ addLocation (choice [ letExpr, caseExpr ])
238+ <|> ifExpr
239+ <|> lambdaExpr
240+ <?> "an expression"
214241
215242
216−ifExpr :: IParser Source.Expr'
243+ifExpr :: IParser Source.Expr
217244ifExpr =
218− do try (reserved "if")
245+ do start <- getMyPosition
246+ try (reserved "if")
219247 whitespace
220− normal <|> multiIf
248+ makeExpr <- normal <|> multiIf
249+ end <- getMyPosition
250+ return (makeExpr (R.Region start end))
221251 where
222252 normal =
223− do bool <- expr
253+ do condition <- expr
224254 padded (reserved "then")
225255 thenBranch <- expr
226256 whitespace <?> "an 'else' branch"
227257 reserved "else" <?> "an 'else' branch"
228258 whitespace
229259 elseBranch <- expr
230− return $ E.MultiIf
231− [ (bool, thenBranch)
232− , (A.sameAs elseBranch (E.Literal . L.Boolean $ True), elseBranch)
233− ]
260+ return $ \region -> A.A region (E.MultiIf [(condition, thenBranch)] elseBranch)
234261
235262 multiIf =
236− E.MultiIf <$> spaceSep1 iff
237− where
238− iff =
239− do string "|" ; whitespace
240− b <- expr ; padded arrow
241− (,) 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)
242276
243277
244278lambdaExpr :: IParser Source.Expr
245279lambdaExpr =
246− do char '\\' <|> char '\x03BB' <?> "anonymous function"
280+ do char '\\' <|> char '\x03BB' <?> "an anonymous function"
247281 whitespace
248282 args <- spaceSep1 Pattern.term
249− padded arrow
283+ padded rightArrow
250284 body <- expr
251285 return (makeFunction args body)
252286
@@ -261,7 +295,7 @@ caseExpr =
261295 where
262296 case_ =
263297 do p <- Pattern.expr
264− padded arrow
298+ padded rightArrow
265299 (,) p <$> expr
266300
267301 with =
src/Parse/Helpers.hs+85−50
@@ -1,7 +1,7 @@
11module Parse.Helpers where
22
33import Prelude hiding (until)
4−import Control.Applicative ((<$>),(<*>))
4+import Control.Applicative ((<$>),(<*>),(<*))
55import Control.Monad (guard, join)
66import Control.Monad.State (State)
77import Data.Char (isUpper)
@@ -18,8 +18,8 @@ import qualified AST.Expression.Source as Source
1818import qualified AST.Helpers as Help
1919import qualified AST.Literal as L
2020import qualified AST.Variable as Variable
21−import Elm.Utils ((|>))
2221import qualified Reporting.Annotation as A
22+import qualified Reporting.Error.Syntax as Syntax
2323import qualified Reporting.Region as R
2424
2525
@@ -37,9 +37,13 @@ reserveds =
3737 ]
3838
3939
40+-- ERROR HELP
41+
4042expecting = flip (<?>)
4143
4244
45+-- SETUP
46+
4347type OpTable = Map.Map String (Int, Decl.Assoc)
4448type SourceM = State SourcePos
4549type IParser a = ParsecT String OpTable SourceM a
@@ -59,17 +63,17 @@ iParseWithTable sourceName table aParser input =
5963
6064var :: IParser String
6165var =
62− makeVar (letter <|> char '_' <?> "variable")
66+ makeVar (letter <|> char '_') <?> "a name"
6367
6468
6569lowVar :: IParser String
6670lowVar =
67− makeVar (lower <?> "lower case variable")
71+ makeVar lower <?> "a lower case name"
6872
6973
7074capVar :: IParser String
7175capVar =
72− makeVar (upper <?> "upper case variable")
76+ makeVar upper <?> "an upper case name"
7377
7478
7579qualifiedVar :: IParser String
@@ -84,20 +88,20 @@ rLabel = lowVar
8488
8589innerVarChar :: IParser Char
8690innerVarChar =
87− alphaNum <|> char '_' <|> char '\'' <?> ""
91+ alphaNum <|> char '_' <|> char '\'' <?> "more letters in this name"
8892
8993
9094makeVar :: IParser Char -> IParser String
91−makeVar p =
92− do v <- (:) <$> p <*> many innerVarChar
93− if v `elem` reserveds
94− then fail $ "unexpected keyword '" ++ v ++ "', variables cannot be keywords"
95− else return v
95+makeVar firstChar =
96+ do variable <- (:) <$> firstChar <*> many innerVarChar
97+ if variable `elem` reserveds
98+ then fail (Syntax.keyword variable)
99+ else return variable
96100
97101
98102reserved :: String -> IParser String
99103reserved word =
100− expecting ("reserved word '" ++ word ++ "'") $
104+ expecting ("reserved word `" ++ word ++ "`") $
101105 do string word
102106 notFollowedBy innerVarChar
103107 return word
@@ -109,7 +113,7 @@ anyOp :: IParser String
109113anyOp =
110114 betwixt '`' '`' qualifiedVar
111115 <|> symOp
112− <?> "infix operator"
116+ <?> "an infix operator like (+)"
113117
114118
115119symOp :: IParser String
@@ -124,13 +128,23 @@ symOp =
124128-- COMMON SYMBOLS
125129
126130equals :: IParser String
127−equals = string "="
131+equals =
132+ string "=" <?> "="
133+
134+
135+rightArrow :: IParser String
136+rightArrow =
137+ string "->" <|> string "\8594" <?> "->"
138+
139+
140+leftArrow :: IParser String
141+leftArrow =
142+ string "<-" <?> "<- for a record update"
128143
129−arrow :: IParser String
130−arrow = string "->" <|> string "\8594" <?> "arrow (->)"
131144
132145hasType :: IParser String
133−hasType = string ":" <?> "':' (a type annotation)'"
146+hasType =
147+ string ":" <?> "the \"has type\" symbol ':'"
134148
135149
136150commitIf check p =
@@ -143,14 +157,19 @@ commitIf check p =
143157-- SEPARATORS
144158
145159spaceySepBy1 :: IParser b -> IParser a -> IParser [a]
146−spaceySepBy1 sep p =
147− do a <- p
148− (a:) <$> many (commitIf (whitespace >> sep) (padded sep >> p))
160+spaceySepBy1 sep parser =
161+ do value <- parser
162+ (value:) <$> spaceyPrefixBy sep parser
163+
164+
165+spaceyPrefixBy :: IParser sep -> IParser a -> IParser [a]
166+spaceyPrefixBy sep parser =
167+ many (commitIf (whitespace >> sep) (padded sep >> parser))
149168
150169
151170comma :: IParser Char
152171comma =
153− char ',' <?> "comma ','"
172+ char ',' <?> "a comma ','"
154173
155174
156175commaSep1 :: IParser a -> IParser [a]
@@ -165,17 +184,17 @@ commaSep =
165184
166185semiSep1 :: IParser a -> IParser [a]
167186semiSep1 =
168− spaceySepBy1 (char ';' <?> "semicolon ';'")
187+ spaceySepBy1 (char ';' <?> "a semicolon ';'")
169188
170189
171190pipeSep1 :: IParser a -> IParser [a]
172191pipeSep1 =
173− spaceySepBy1 (char '|' <?> "type divider '|'")
192+ spaceySepBy1 (char '|' <?> "a vertical bar '|'")
174193
175194
176195consSep1 :: IParser a -> IParser [a]
177196consSep1 =
178− spaceySepBy1 (string "::" <?> "cons operator '::'")
197+ spaceySepBy1 (string "::" <?> "a cons operator '::'")
179198
180199
181200dotSep1 :: IParser a -> IParser [a]
@@ -192,15 +211,15 @@ spacePrefix p =
192211 constrainedSpacePrefix p (\_ -> return ())
193212
194213
195−constrainedSpacePrefix p constraint =
214+constrainedSpacePrefix parser constraint =
196215 many $ choice
197− [ try (spacing >> lookAhead (oneOf "[({")) >> p
198− , try (spacing >> p)
216+ [ try (spacing >> lookAhead (oneOf "[({")) >> parser
217+ , try (spacing >> parser)
199218 ]
200219 where
201220 spacing = do
202221 n <- whitespace
203− constraint n
222+ constraint n <?> Syntax.whitespace
204223 indented
205224
206225
@@ -212,17 +231,19 @@ followedBy a b =
212231 return x
213232
214233
234+betwixt :: Char -> Char -> IParser a -> IParser a
215235betwixt a b c =
216236 do char a
217237 out <- c
218− char b <?> "closing '" ++ [b] ++ "'"
238+ char b <?> "a closing '" ++ [b] ++ "'"
219239 return out
220240
221241
242+surround :: Char -> Char -> String -> IParser a -> IParser a
222243surround a z name p = do
223244 char a
224245 v <- padded p
225− char z <?> unwords ["closing", name, show z]
246+ char z <?> unwords ["a closing", name, show z]
226247 return v
227248
228249
@@ -268,7 +289,7 @@ accessible exprParser =
268289
269290 annotatedRootExpr@(A.A _ rootExpr) <- exprParser
270291
271− access <- optionMaybe (try dot <?> "field access (e.g. List.map)")
292+ access <- optionMaybe (try dot <?> "a field access like .name")
272293
273294 case access of
274295 Nothing ->
@@ -276,7 +297,7 @@ accessible exprParser =
276297
277298 Just _ ->
278299 accessible $
279− do v <- var <?> "field access (e.g. List.map)"
300+ do v <- var
280301 end <- getMyPosition
281302 return . A.at start end $
282303 case rootExpr of
@@ -289,7 +310,8 @@ accessible exprParser =
289310
290311dot :: IParser ()
291312dot =
292− char '.' >> notFollowedBy (char '.')
313+ do char '.'
314+ notFollowedBy (char '.')
293315
294316
295317-- WHITESPACE
@@ -304,7 +326,7 @@ padded p =
304326
305327spaces :: IParser String
306328spaces =
307− let space = string " " <|> multiComment <?> "whitespace"
329+ let space = string " " <|> multiComment <?> Syntax.whitespace
308330 in
309331 concat <$> many1 space
310332
@@ -333,14 +355,14 @@ whitespace =
333355
334356freshLine :: IParser [[String]]
335357freshLine =
336− try (many1 newline >> many space_nl) <|> try (many1 space_nl) <?> ""
358+ try (many1 newline >> many space_nl) <|> try (many1 space_nl) <?> Syntax.freshLine
337359 where
338360 space_nl = try $ spaces >> many1 newline
339361
340362
341363newline :: IParser String
342364newline =
343− simpleNewline <|> lineComment <?> "a newline"
365+ simpleNewline <|> lineComment <?> Syntax.newline
344366
345367
346368simpleNewline :: IParser String
@@ -355,16 +377,23 @@ lineComment =
355377 return ("--" ++ comment)
356378
357379
380+docComment :: IParser String
381+docComment =
382+ do try (string "{-|")
383+ contents <- closeComment
384+ return (init (init contents))
385+
386+
358387multiComment :: IParser String
359388multiComment =
360− (++) <$> try (string "{-") <*> closeComment
389+ (++) <$> try (string "{-" <* notFollowedBy (string "|")) <*> closeComment
361390
362391
363392closeComment :: IParser String
364393closeComment =
365394 anyUntil $
366395 choice $
367− [ try (string "-}") <?> "close comment"
396+ [ try (string "-}") <?> "the end of a comment -}"
368397 , concat <$> sequence [ try (string "{-"), closeComment, closeComment ]
369398 ]
370399
@@ -404,6 +433,7 @@ ignoreUntil end =
404433 choice
405434 [ try (ignore chr) <|> ignore str
406435 , ignore multiComment
436+ , ignore docComment
407437 , ignore anyChar
408438 ]
409439
@@ -452,14 +482,16 @@ shader =
452482 case glSource rawSrc of
453483 Left err -> parserFail . show $ err
454484 Right tipe -> return (rawSrc, tipe)
455− where
456− closeShader builder =
457− choice
458− [ do try (string "|]")
459− return (builder "")
460− , do c <- anyChar
461− closeShader (builder . (c:))
462− ]
485+
486+
487+closeShader :: (String -> a) -> IParser a
488+closeShader builder =
489+ choice
490+ [ do try (string "|]")
491+ return (builder "")
492+ , do c <- anyChar
493+ closeShader (builder . (c:))
494+ ]
463495
464496
465497glSource :: String -> Either ParseError L.GLShaderTipe
@@ -472,6 +504,8 @@ glSource src =
472504 |> foldr addGLinput emptyDecls
473505 |> Right
474506 where
507+ (|>) = flip ($)
508+
475509 emptyDecls = L.GLShaderTipe Map.empty Map.empty Map.empty
476510
477511 addGLinput (qual,tipe,name) glDecls =
@@ -513,9 +547,10 @@ glSource src =
513547
514548
515549str :: IParser String
516−str = expecting "String" $ do
517− s <- choice [ multiStr, singleStr ]
518− processAs T.stringLiteral . sandwich '\"' $ concat s
550+str =
551+ expecting "a string" $
552+ do s <- choice [ multiStr, singleStr ]
553+ processAs T.stringLiteral . sandwich '\"' $ concat s
519554 where
520555 rawString quote insides =
521556 quote >> manyTill insides quote
@@ -555,7 +590,7 @@ escaped delim =
555590
556591chr :: IParser Char
557592chr =
558− betwixt '\'' '\'' character <?> "character"
593+ betwixt '\'' '\'' character <?> "a character"
559594 where
560595 nonQuote = satisfy (/='\'')
561596
src/Parse/Literal.hs+4−4
@@ -9,19 +9,19 @@ import qualified AST.Literal as L
99
1010literal :: IParser L.Literal
1111literal =
12− num <|> (L.Str <$> str) <|> (L.Chr <$> chr) <?> "literal"
12+ num <|> (L.Str <$> str) <|> (L.Chr <$> chr)
1313
1414
1515num :: IParser L.Literal
1616num =
17− toLiteral <$> (rawNumber <?> "number")
17+ toLiteral <$> (rawNumber <?> "a number")
1818
1919
2020toLiteral :: String -> L.Literal
2121toLiteral n
22− | 'x' `elem` n = L.IntNum (read n)
22+ | 'x' `elem` n = L.IntNum (read n)
2323 | any (`elem` ".eE") n = L.FloatNum (read n)
24− | otherwise = L.IntNum (read n)
24+ | otherwise = L.IntNum (read n)
2525
2626
2727rawNumber :: IParser String
src/Parse/Module.hs+20−11
@@ -6,6 +6,7 @@ import Text.Parsec hiding (newline, spaces)
66import Parse.Helpers
77import qualified AST.Module as Module
88import qualified AST.Variable as Var
9+import Reporting.Annotation as A
910
1011
1112getModuleName :: String -> Maybe String
@@ -25,18 +26,24 @@ header =
2526 do optional freshLine
2627 (names, exports) <-
2728 option (["Main"], Var.openListing) (moduleDecl `followedBy` freshLine)
29+ docs <-
30+ choice
31+ [ addLocation (Just <$> docComment) `followedBy` freshLine
32+ , addLocation (return Nothing)
33+ ]
2834 imports' <- imports
29− return (Module.Header names exports imports')
35+ return (Module.Header names docs exports imports')
3036
3137
32−moduleDecl :: IParser ([String], Var.Listing Var.Value)
38+moduleDecl :: IParser ([String], Var.Listing (A.Located Var.Value))
3339moduleDecl =
40+ expecting "a module declaration" $
3441 do try (reserved "module")
3542 whitespace
36− names <- dotSep1 capVar <?> "name of module"
43+ names <- dotSep1 capVar <?> "the name of this module"
44+ whitespace
45+ exports <- option Var.openListing (listing (addLocation value))
3746 whitespace
38− exports <- option Var.openListing (listing value)
39− whitespace <?> "reserved word 'where'"
4047 reserved "where"
4148 return (names, exports)
4249
@@ -48,6 +55,7 @@ imports =
4855
4956import' :: IParser Module.UserImport
5057import' =
58+ expecting "an import" $
5159 addLocation $
5260 do try (reserved "import")
5361 whitespace
@@ -57,14 +65,14 @@ import' =
5765 method :: String -> IParser Module.ImportMethod
5866 method defaultAlias =
5967 Module.ImportMethod
60− <$> option (Just defaultAlias) (Just <$> as')
68+ <$> option (Just defaultAlias) (Just <$> as' defaultAlias)
6169 <*> option Var.closedListing exposing
6270
63− as' :: IParser String
64− as' =
71+ as' :: String -> IParser String
72+ as' moduleName =
6573 do try (whitespace >> reserved "as")
6674 whitespace
67− capVar <?> "alias for module"
75+ capVar <?> ("an alias for module `" ++ moduleName ++ "`")
6876
6977 exposing :: IParser (Var.Listing Var.Value)
7078 exposing =
@@ -75,13 +83,14 @@ import' =
7583
7684listing :: IParser a -> IParser (Var.Listing a)
7785listing item =
86+ expecting "a listing of values and types to expose, like (..)" $
7887 do try (whitespace >> char '(')
7988 whitespace
8089 listing <-
8190 choice
8291 [ const Var.openListing <$> string ".."
8392 , Var.Listing <$> commaSep1 item <*> return False
84− ] <?> "listing of values (x,y,z)"
93+ ]
8594 whitespace
8695 char ')'
8796 return listing
@@ -89,7 +98,7 @@ listing item =
8998
9099value :: IParser Var.Value
91100value =
92− val <|> tipe
101+ val <|> tipe <?> "a value or type to expose"
93102 where
94103 val =
95104 Var.Value <$> (lowVar <|> parens symOp)
src/Parse/Parse.hs+11−10
@@ -1,10 +1,10 @@
11{-# OPTIONS_GHC -Wall #-}
2−module Parse.Parse (program) where
2+module Parse.Parse (program, parse) where
33
44import Control.Applicative ((<$>), (<*>))
55import qualified Data.Map as Map
66import qualified Data.Traversable as T
7−import Text.Parsec (char, eof, letter, many, optional, putState, (<|>), (<?>))
7+import Text.Parsec (char, eof, letter, many, optional, putState, (<|>))
88import qualified Text.Parsec.Error as Parsec
99
1010import qualified AST.Declaration as D
@@ -21,36 +21,37 @@ import qualified Validate
2121
2222program
2323 :: Bool
24+ -> Bool
2425 -> OpTable
2526 -> String
2627 -> Result.Result wrn Error.Error M.ValidModule
27−program needsDefaults table src =
28− do (M.Module names filePath exports imports sourceDecls) <-
28+program needsDefaults isRoot table src =
29+ do (M.Module names filePath docs exports imports sourceDecls) <-
2930 parseWithTable table src programParser
3031
31− validDecls <- Validate.declarations sourceDecls
32+ validDecls <- Validate.declarations isRoot sourceDecls
3233
3334 let ammendedImports =
3435 (if needsDefaults then Imports.defaults else [], imports)
3536
36− return (M.Module names filePath exports ammendedImports validDecls)
37+ return (M.Module names filePath docs exports ammendedImports validDecls)
3738
3839
3940-- HEADERS AND DECLARATIONS
4041
4142programParser :: IParser M.SourceModule
4243programParser =
43− do (M.Header names exports imports) <- Module.header
44+ do (M.Header names docs exports imports) <- Module.header
4445 decls <- declarations
4546 optional freshLine
4647 optional spaces
4748 eof
48− return $ M.Module names "" exports imports decls
49+ return $ M.Module names "" docs exports imports decls
4950
5051
5152declarations :: IParser [D.SourceDecl]
5253declarations =
53− (:) <$> (Decl.declaration <?> "at least one datatype or variable definition")
54+ (:) <$> Decl.declaration
5455 <*> many freshDef
5556
5657
@@ -58,7 +59,7 @@ freshDef :: IParser D.SourceDecl
5859freshDef =
5960 commitIf (freshLine >> (letter <|> char '_')) $
6061 do _ <- freshLine
61− Decl.declaration <?> "another datatype or variable definition"
62+ Decl.declaration
6263
6364
6465-- RUN PARSERS
src/Parse/Pattern.hs+2−2
@@ -89,7 +89,7 @@ list =
8989term :: IParser P.RawPattern
9090term =
9191 choice [ record, tuple, list, basic ]
92− <?> "pattern"
92+ <?> "a pattern"
9393
9494
9595patternConstructor :: IParser P.RawPattern
@@ -104,7 +104,7 @@ patternConstructor =
104104
105105expr :: IParser P.RawPattern
106106expr =
107− asPattern subPattern <?> "pattern"
107+ asPattern subPattern <?> "a pattern"
108108 where
109109 subPattern =
110110 do patterns <- consSep1 (patternConstructor <|> term)
src/Parse/Type.hs+3−3
@@ -15,7 +15,7 @@ import qualified Reporting.Region as R
1515tvar :: IParser Type.Raw
1616tvar =
1717 addLocation
18− (Type.RVar <$> lowVar <?> "type variable")
18+ (Type.RVar <$> lowVar <?> "a type variable")
1919
2020
2121tuple :: IParser Type.Raw
@@ -71,7 +71,7 @@ term =
7171app :: IParser Type.Raw
7272app =
7373 do start <- getMyPosition
74− f <- constructor0 <|> try tupleCtor <?> "type constructor"
74+ f <- constructor0 <|> try tupleCtor <?> "a type constructor"
7575 args <- spacePrefix term
7676 end <- getMyPosition
7777 case args of
@@ -89,7 +89,7 @@ expr :: IParser Type.Raw
8989expr =
9090 do start <- getMyPosition
9191 t1 <- app <|> term
92− arr <- optionMaybe $ try (whitespace >> arrow)
92+ arr <- optionMaybe $ try (whitespace >> rightArrow)
9393 case arr of
9494 Nothing ->
9595 return t1
src/Reporting/Annotation.hs+6−2
@@ -16,6 +16,10 @@ type Located a =
1616 Annotated R.Region a
1717
1818
19+type Commented a =
20+ Annotated (R.Region, Maybe String) a
21+
22+
1923-- CREATE
2024
2125at :: R.Position -> R.Position -> a -> Located a
@@ -48,5 +52,5 @@ drop (A _ value) =
4852-- PRETTY PRINT
4953
5054instance (P.Pretty a) => P.Pretty (Annotated info a) where
51− pretty parens (A _ value) =
52− P.pretty parens value
55+ pretty dealiaser parens (A _ value) =
56+ P.pretty dealiaser parens value
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.hs+60−13
@@ -1,10 +1,17 @@
1+{-# OPTIONS_GHC -Wall #-}
12{-# LANGUAGE OverloadedStrings #-}
23module Reporting.Error where
34
5+import Data.Aeson ((.=))
6+import qualified Data.Aeson as Json
7+import Prelude hiding (print)
8+
49import qualified Reporting.Annotation as A
510import qualified Reporting.Error.Canonicalize as Canonicalize
11+import qualified Reporting.Error.Docs as Docs
612import qualified Reporting.Error.Syntax as Syntax
713import qualified Reporting.Error.Type as Type
14+import qualified Reporting.PrettyPrint as P
815import qualified Reporting.Report as Report
916
1017
@@ -14,28 +21,68 @@ data Error
1421 = Syntax Syntax.Error
1522 | Canonicalize Canonicalize.Error
1623 | Type Type.Error
24+ | Docs Docs.Error
25+
26+
27+-- TO REPORT
28+
29+toReport :: P.Dealiaser -> Error -> Report.Report
30+toReport dealiaser err =
31+ case err of
32+ Syntax syntaxError ->
33+ Syntax.toReport dealiaser syntaxError
34+
35+ Canonicalize canonicalizeError ->
36+ Canonicalize.toReport dealiaser canonicalizeError
37+
38+ Type typeError ->
39+ Type.toReport dealiaser typeError
40+
41+ Docs docsError ->
42+ Docs.toReport docsError
1743
1844
1945-- TO STRING
2046
21−toString :: String -> String -> A.Located Error -> String
22−toString location source (A.A region err) =
47+toString :: P.Dealiaser -> String -> String -> A.Located Error -> String
48+toString dealiaser location source (A.A region err) =
49+ Report.toString location region (toReport dealiaser err) source
50+
51+
52+print :: P.Dealiaser -> String -> String -> A.Located Error -> IO ()
53+print dealiaser location source (A.A region err) =
54+ Report.printError location region (toReport dealiaser err) source
55+
56+
57+-- TO JSON
58+
59+toJson :: P.Dealiaser -> FilePath -> A.Located Error -> Json.Value
60+toJson dealiaser filePath (A.A region err) =
2361 let
24− (tag, report) =
62+ (maybeRegion, additionalFields) =
2563 case err of
2664 Syntax syntaxError ->
27− ( "SYNTAX ERROR"
28− , Syntax.toReport syntaxError
29− )
65+ Report.toJson [] (Syntax.toReport dealiaser syntaxError)
3066
3167 Canonicalize canonicalizeError ->
32− ( "NAMING ERROR"
33− , Canonicalize.toReport canonicalizeError
34− )
68+ let
69+ suggestions =
70+ maybe []
71+ (\s -> ["suggestions" .= s])
72+ (Canonicalize.extractSuggestions canonicalizeError)
73+ in
74+ Report.toJson suggestions (Canonicalize.toReport dealiaser canonicalizeError)
3575
3676 Type typeError ->
37− ( "TYPE ERROR"
38− , Type.toReport typeError
39− )
77+ Report.toJson [] (Type.toReport dealiaser typeError)
78+
79+ Docs docsError ->
80+ Report.toJson [] (Docs.toReport docsError)
4081 in
41− Report.toString tag location region report source
82+ Json.object $
83+ [ "file" .= filePath
84+ , "region" .= region
85+ , "subregion" .= maybeRegion
86+ , "type" .= ("error" :: String)
87+ ]
88+ ++ additionalFields
src/Reporting/Error/Canonicalize.hs+68−67
@@ -1,16 +1,12 @@
11{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE OverloadedStrings #-}
32module Reporting.Error.Canonicalize where
43
5−import Data.Function (on)
6−import qualified Data.List as List
7−import qualified Text.EditDistance as Dist
84import qualified Text.PrettyPrint as P
95
106import qualified AST.Module as Module
117import qualified AST.Type as Type
128import qualified AST.Variable as Var
13−import Elm.Utils ((|>))
9+import qualified Reporting.Error.Helpers as Help
1410import qualified Reporting.PrettyPrint as P
1511import qualified Reporting.Region as Region
1612import qualified Reporting.Report as Report
@@ -37,8 +33,8 @@ data VarError = VarError
3733
3834data VarProblem
3935 = Ambiguous
40− | UnknownQualifier String
41− | QualifiedUnknown String
36+ | UnknownQualifier String String
37+ | QualifiedUnknown String String
4238 | ExposedUnknown
4339
4440
@@ -110,53 +106,43 @@ port name isInbound rootType localType maybeMessage =
110106 Port (PortError name isInbound rootType localType maybeMessage)
111107
112108
113−-- NEARBY NAMES
114−
115−nearbyNames :: (a -> String) -> a -> [a] -> [a]
116−nearbyNames format name names =
117− let editDistance =
118− if length (format name) < 3 then 1 else 2
119− in
120− names
121− |> map (\x -> (distance (format name) (format x), x))
122− |> List.sortBy (compare `on` fst)
123− |> filter ( (<= editDistance) . abs . fst )
124− |> map snd
125−
126−
127−distance :: String -> String -> Int
128−distance x y =
129− Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
109+-- TO REPORT
130110
111+namingError :: String -> String -> Report.Report
112+namingError pre post =
113+ Report.simple "NAMING ERROR" pre post
131114
132−-- TO REPORT
133115
134−toReport :: Error -> Report.Report
135−toReport err =
116+toReport :: P.Dealiaser -> Error -> Report.Report
117+toReport dealiaser err =
136118 case err of
137119 Var (VarError kind name problem suggestions) ->
138− let var = kind ++ " '" ++ name ++ "'"
120+ let var = kind ++ " `" ++ name ++ "`"
139121 in
140122 case problem of
141123 Ambiguous ->
142− Report.simple
124+ namingError
143125 ("This usage of " ++ var ++ " is ambiguous.")
144− (maybeYouWant suggestions)
145−
146− UnknownQualifier qualifier ->
147− Report.simple
148− ("Cannot find " ++ var ++ ", qualifier '" ++ qualifier ++ "' is not in scope.")
149− (maybeYouWant suggestions)
150−
151− QualifiedUnknown qualifier ->
152− Report.simple
153− ("Cannot find " ++ var ++ ", module '" ++ qualifier ++ "' does not expose that value.")
154− (maybeYouWant suggestions)
126+ (Help.maybeYouWant suggestions)
127+
128+ UnknownQualifier qualifier localName ->
129+ namingError
130+ ("Cannot find " ++ var ++ ".")
131+ ( "The qualifier `" ++ qualifier ++ "` is not in scope. "
132+ ++ Help.maybeYouWant (map (\modul -> modul ++ "." ++ localName) suggestions)
133+ )
134+
135+ QualifiedUnknown qualifier localName ->
136+ namingError
137+ ("Cannot find " ++ var ++ ".")
138+ ( "`" ++ qualifier ++ "` does not expose `" ++ localName ++ "`. "
139+ ++ Help.maybeYouWant (map (\v -> qualifier ++ "." ++ v) suggestions)
140+ )
155141
156142 ExposedUnknown ->
157− Report.simple
143+ namingError
158144 ("Cannot find " ++ var)
159− (maybeYouWant suggestions)
145+ (Help.maybeYouWant suggestions)
160146
161147 Pattern patternError ->
162148 case patternError of
@@ -169,18 +155,18 @@ toReport err =
169155 argMismatchReport "Type" var expected actual
170156
171157 SelfRecursive name tvars tipe ->
172− Report.simple "This type alias is recursive, forming an infinite type!" $
158+ Report.simple "ALIAS PROBLEM" "This type alias is recursive, forming an infinite type!" $
173159 "Type aliases are just names for more fundamental types, so I go through\n"
174160 ++ "and expand them all to know the real underlying type. The problem is that\n"
175161 ++ "when I expand a recursive type alias, it keeps getting bigger and bigger.\n"
176162 ++ "Dealiasing results in an infinitely large type! Try this instead:\n\n"
177163 ++ " type " ++ name ++ concatMap (' ':) tvars ++ " ="
178− ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty True tipe)))
164+ ++ P.render (P.nest 8 (P.hang (P.text name) 2 (P.pretty dealiaser True tipe)))
179165 ++ "\n\nThis creates a brand new type that cannot be expanded. It is similar types\n"
180166 ++ "like List which are recursive, but do not get expanded into infinite types."
181167
182168 MutuallyRecursive aliases ->
183− Report.simple "This type alias is part of a mutually recursive set of type aliases." $
169+ Report.simple "ALIAS PROBLEM" "This type alias is part of a mutually recursive set of type aliases." $
184170 "The following type aliases all depend on each other in some way:\n"
185171 ++ concatMap showName aliases ++ "\n\n"
186172 ++ "The problem is that when you try to expand any of these type aliases, they\n"
@@ -195,19 +181,19 @@ toReport err =
195181 in
196182 case importError of
197183 ModuleNotFound suggestions ->
198− Report.simple
199− ("Could not find a module named '" ++ moduleName ++ "'")
200− (maybeYouWant (map Module.nameToString suggestions))
184+ namingError
185+ ("Could not find a module named `" ++ moduleName ++ "`")
186+ (Help.maybeYouWant (map Module.nameToString suggestions))
201187
202188 ValueNotFound value suggestions ->
203− Report.simple
204− ("Module '" ++ moduleName ++ "' does not expose '" ++ value ++ "'")
205− (maybeYouWant suggestions)
189+ namingError
190+ ("Module `" ++ moduleName ++ "` does not expose `" ++ value ++ "`")
191+ (Help.maybeYouWant suggestions)
206192
207193 Export name suggestions ->
208− Report.simple
209− ("Could not export '" ++ name ++ "' which is not defined in this module.")
210− (maybeYouWant suggestions)
194+ namingError
195+ ("Could not export `" ++ name ++ "` which is not defined in this module.")
196+ (Help.maybeYouWant suggestions)
211197
212198 Port (PortError name isInbound _rootType localType maybeMessage) ->
213199 let
@@ -216,16 +202,16 @@ toReport err =
216202
217203 preHint =
218204 "Trying to send " ++ maybe "an unsupported type" id maybeMessage
219− ++ " through " ++ boundPort ++ " '" ++ name ++ "'"
205+ ++ " through " ++ boundPort ++ " `" ++ name ++ "`"
220206
221207 postHint =
222208 "The specific unsupported type is:\n\n"
223− ++ P.render (P.nest 4 (P.pretty False localType)) ++ "\n\n"
209+ ++ P.render (P.nest 4 (P.pretty dealiaser False localType)) ++ "\n\n"
224210 ++ "The types of values that can flow through " ++ boundPort ++ "s include:\n"
225211 ++ "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, Tuples,\n"
226212 ++ "Json.Values, and concrete records."
227213 in
228− Report.simple preHint postHint
214+ Report.simple "PORT ERROR" preHint postHint
229215
230216
231217argMismatchReport :: String -> Var.Canonical -> Int -> Int -> Report.Report
@@ -234,21 +220,36 @@ argMismatchReport kind var expected actual =
234220 preHint =
235221 kind ++ " " ++ Var.toString var ++ " has too "
236222 ++ (if actual < expected then "few" else "many")
237− ++ "arguments."
223+ ++ " arguments."
238224
239225 postHint =
240226 "Expecting " ++ show expected ++ ", but got " ++ show actual ++ "."
241227 in
242− Report.Report Nothing preHint postHint
228+ Report.simple "ARGUMENT" preHint postHint
243229
244230
245−maybeYouWant :: [String] -> String
246−maybeYouWant suggestions =
247− case suggestions of
248− [] ->
249− ""
231+extractSuggestions :: Error -> Maybe [String]
232+extractSuggestions err =
233+ case err of
234+ Var (VarError _ _ _ suggestions) ->
235+ Just suggestions
236+
237+ Pattern _ ->
238+ Nothing
239+
240+ Alias _ ->
241+ Nothing
242+
243+ Import _ importError ->
244+ case importError of
245+ ModuleNotFound suggestions ->
246+ Just (map Module.nameToString suggestions)
247+
248+ ValueNotFound _ suggestions ->
249+ Just suggestions
250250
251− _:_ ->
252− "Maybe you want one of the following?\n"
253− ++ concatMap ("\n "++) suggestions
251+ Export _ suggestions ->
252+ Just suggestions
254253
254+ Port _ ->
255+ Nothing
src/Reporting/Error/Docs.hsnew file+61
@@ -0,0 +1,61 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Reporting.Error.Docs where
3+
4+import qualified Reporting.Error.Helpers as Help
5+import qualified Reporting.Report as Report
6+
7+
8+data Error
9+ = NoDocs
10+ | OnlyInDocs String [String]
11+ | OnlyInExports [String]
12+ | NoComment String
13+ | NoType String
14+
15+
16+-- TO REPORT
17+
18+toReport :: Error -> Report.Report
19+toReport err =
20+ case err of
21+ NoDocs ->
22+ Report.simple "DOCUMENTATION ERROR"
23+ ( "You must have a documentation comment between the module declaration and the\n"
24+ ++ "imports."
25+ )
26+ "Learn how at <http://package.elm-lang.org/help/documentation-format>"
27+
28+ OnlyInDocs name suggestions ->
29+ Report.simple "DOCUMENTATION ERROR"
30+ ("Your module documentation includes `" ++ name ++ "` which is not exported.")
31+ ("Is it misspelled? Should it be exported? " ++ Help.maybeYouWant suggestions)
32+
33+ OnlyInExports names ->
34+ Report.simple
35+ "DOCUMENTATION ERROR"
36+ ( "The following exports do not appear in your module documentation:\n"
37+ ++ concatMap ("\n " ++) names
38+ )
39+ ( "All exports must be listed in the module documentation after a @docs keyword.\n"
40+ ++ "Learn how at <http://package.elm-lang.org/help/documentation-format>"
41+ )
42+
43+ NoComment name ->
44+ Report.simple "DOCUMENTATION ERROR"
45+ ("The value `" ++ name ++ "` does not have a documentation comment.")
46+ ( "Documentation comments start with {-| and end with -}. They should provide a\n"
47+ ++ "clear description of how they work, and ideally a small code example. This is\n"
48+ ++ "extremely valuable for users checking out your package!\n\n"
49+ ++ "If you think the docs are clearer without any words, you can use an empty\n"
50+ ++ "comment {-|-} which should be used sparingly. Maybe you have a section of 20\n"
51+ ++ "values all with the exact same type. The docs may read better if they are all\n"
52+ ++ "described in one place.\n\n"
53+ ++ "Learn more at <http://package.elm-lang.org/help/documentation-format>"
54+ )
55+
56+ NoType name ->
57+ Report.simple "MISSING ANNOTATION"
58+ ("The value `" ++ name ++ "` does not have a type annotation.")
59+ ( "Adding type annotations is best practice and it gives you a chance to name\n"
60+ ++ "types and type variables so they are as easy as possible to understand!"
61+ )
src/Reporting/Error/Helpers.hsnew file+39
@@ -0,0 +1,39 @@
1+{-# OPTIONS_GHC -Wall #-}
2+module Reporting.Error.Helpers where
3+
4+import Data.Function (on)
5+import qualified Data.List as List
6+import qualified Text.EditDistance as Dist
7+
8+import Elm.Utils ((|>))
9+
10+
11+-- NEARBY NAMES
12+
13+nearbyNames :: (a -> String) -> a -> [a] -> [a]
14+nearbyNames format name names =
15+ let editDistance =
16+ if length (format name) < 3 then 1 else 2
17+ in
18+ names
19+ |> map (\x -> (distance (format name) (format x), x))
20+ |> List.sortBy (compare `on` fst)
21+ |> filter ( (<= editDistance) . abs . fst )
22+ |> map snd
23+
24+
25+distance :: String -> String -> Int
26+distance x y =
27+ Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
28+
29+
30+maybeYouWant :: [String] -> String
31+maybeYouWant suggestions =
32+ case suggestions of
33+ [] ->
34+ ""
35+
36+ _:_ ->
37+ "Maybe you want one of the following?\n"
38+ ++ concatMap ("\n "++) (take 4 suggestions)
39+
src/Reporting/Error/Nitpick.hsdeleted−25
@@ -1,25 +0,0 @@
1−module Reporting.Error.Nitpick where
2−
3−import qualified Data.Aeson as Json
4−
5−import qualified AST.Variable as Var
6−import qualified Reporting.Region as R
7−
8−
9−data Error = ConstructorTypeNotFound Var.Canonical
10−
11−-- TO STRING
12−
13−toString :: R.Region -> Error -> String
14−toString region err =
15− case err of
16− _ -> error "Nitpick.toString" region
17−
18−
19−-- TO JSON
20−
21−toJson :: Error -> Json.Value
22−toJson err =
23− case err of
24− _ -> error "Nitpick.toJson"
25−
src/Reporting/Error/Syntax.hs+130−48
@@ -2,13 +2,13 @@
22module Reporting.Error.Syntax where
33
44import qualified Data.List as List
5+import qualified Data.Set as Set
56import qualified Text.Parsec.Error as Parsec
67import qualified Text.PrettyPrint as P
78import Text.PrettyPrint ((<+>))
89
910import qualified AST.Helpers as Help
1011import qualified AST.Type as Type
11−import Elm.Utils ((|>))
1212import qualified Reporting.PrettyPrint as P
1313import qualified Reporting.Report as Report
1414
@@ -18,6 +18,7 @@ data Error
1818 | InfixDuplicate String
1919 | TypeWithoutDefinition String
2020 | PortWithoutAnnotation String
21+ | UnexpectedPort
2122 | DuplicateValueDeclaration String
2223 | DuplicateTypeDeclaration String
2324 | DuplicateDefinition String
@@ -27,16 +28,15 @@ data Error
2728
2829-- TO REPORT
2930
30−toReport :: Error -> Report.Report
31−toReport err =
31+toReport :: P.Dealiaser -> Error -> Report.Report
32+toReport dealiaser err =
3233 case err of
3334 Parse messages ->
34− Report.simple
35− "Problem when parsing your code!"
36− (unlines (map parseErrorHint messages))
35+ parseErrorReport messages
3736
3837 InfixDuplicate opName ->
3938 Report.simple
39+ "INFIX OVERLAP"
4040 ("The infix declarations for " ++ operator ++ " must be removed.")
4141 ("The precedence and associativity can only be set in one place, and\n"
4242 ++ "this information has already been set somewhere else."
@@ -49,8 +49,9 @@ toReport err =
4949
5050 TypeWithoutDefinition valueName ->
5151 Report.simple
52− ("There is a type annotation for '" ++ valueName ++ "' but there"
53− ++ "is no corresponding definition!"
52+ "MISSING DEFINITION"
53+ ("There is a type annotation for `" ++ valueName ++ "` but there"
54+ ++ " is no corresponding definition!"
5455 )
5556 ("Directly below the type annotation, put a definition like:\n\n"
5657 ++ " " ++ valueName ++ " = 42"
@@ -58,35 +59,49 @@ toReport err =
5859
5960 PortWithoutAnnotation portName ->
6061 Report.simple
61− ("Port '" ++ portName ++ "' does not have a type annotation!")
62+ "PORT ERROR"
63+ ("Port `" ++ portName ++ "` does not have a type annotation!")
6264 ("Directly above the port definition, I need something like this:\n\n"
6365 ++ " port " ++ portName ++ " : Signal Int"
6466 )
6567
68+
69+ UnexpectedPort ->
70+ Report.simple
71+ "PORT ERROR"
72+ "This module has ports, but ports can only appear in the main module."
73+ ( "Ports in library code would create hidden dependencies where importing a\n"
74+ ++ "module could bring in constraints not captured in the public API. Furthermore,\n"
75+ ++ "if the module is imported twice, do we send values out the port twice?"
76+ )
77+
6678 DuplicateValueDeclaration name ->
6779 Report.simple
68− ("Naming multiple top-level values '" ++ name ++ "' makes things\n"
69− ++ "ambiguous. When you say '" ++ name ++ "' which one do you want?!"
80+ "DUPLICATE DEFINITION"
81+ ("Naming multiple top-level values `" ++ name ++ "` makes things\n"
82+ ++ "ambiguous. When you say `" ++ name ++ "` which one do you want?"
7083 )
71− ("Find all the top-level values named '" ++ name ++ "' and\n"
84+ ("Find all the top-level values named `" ++ name ++ "` and\n"
7285 ++ "do some renaming. Make sure the names are distinct!"
7386 )
7487
7588 DuplicateTypeDeclaration name ->
7689 Report.simple
77− ("Naming multiple types '" ++ name ++ "' makes things ambiguous\n"
78− ++ "When you say '" ++ name ++ "' which one do you want?!"
90+ "DUPLICATE DEFINITION"
91+ ("Naming multiple types `" ++ name ++ "` makes things ambiguous\n"
92+ ++ "When you say `" ++ name ++ "` which one do you want?"
7993 )
80− ("Find all the types named '" ++ name ++ "' and\n"
94+ ("Find all the types named `" ++ name ++ "` and\n"
8195 ++ "do some renaming. Make sure the names are distinct!"
8296 )
8397
8498 DuplicateDefinition name ->
8599 Report.simple
86− ("Naming multiple values '" ++ name ++ "' in a single let-expression makes\n"
87− ++ "things ambiguous. When you say '" ++ name ++ "' which one do you want?!"
100+ "DUPLICATE DEFINITION"
101+ ("Naming multiple values `" ++ name ++ "` in a single let-expression makes\n"
102+ ++ "things ambiguous. When you say `" ++ name ++ "` which one do you want?"
88103 )
89− ("Find all the values named '" ++ name ++ "' in this let-expression and\n"
104+ ("Find all the values named `" ++ name ++ "` in this let-expression and\n"
90105 ++ "do some renaming. Make sure the names are distinct!"
91106 )
92107
@@ -95,7 +110,7 @@ toReport err =
95110 P.hang
96111 (P.text "type alias" <+> P.text typeName <+> P.hsep vars <+> P.equals)
97112 4
98− (P.pretty False tipe)
113+ (P.pretty dealiaser False tipe)
99114 where
100115 vars = map P.text (givenVars ++ tvar : tvars)
101116
@@ -110,40 +125,107 @@ toReport err =
110125 |> P.nest 4
111126 ]
112127 where
128+ (|>) = flip ($)
113129 vars = map P.text (givenVars ++ tvar : tvars)
114130 toDoc (ctor, args) =
115− P.text ctor <+> P.hsep (map (P.pretty True) args)
131+ P.text ctor <+> P.hsep (map (P.pretty dealiaser True) args)
116132
117133
118134unboundTypeVars :: String -> String -> [String] -> String -> Report.Report
119135unboundTypeVars typeName tvar tvars revisedDeclaration =
136+ Report.simple
137+ "UNBOUND TYPE VARS"
138+ ( "Not all type variables in `" ++ typeName ++ "` are listed, making sneaky\n"
139+ ++ "type errors possible. Unbound type variables include: "
140+ ++ List.intercalate ", " (tvar:tvars)
141+ )
142+ ( "You probably want this definition instead:\n"
143+ ++ concatMap ("\n "++) (lines revisedDeclaration) ++ "\n\n"
144+ ++ "Here's why. Imagine one `" ++ typeName ++ "` where `" ++ tvar ++ "` is an Int and\n"
145+ ++ "another where it is a Bool. When we explicitly list the type variables, type\n"
146+ ++ "checker can see that they are actually different types."
147+ )
148+
149+
150+-- TAGGING PARSE ERRORS
151+
152+newline :: String
153+newline = "NEWLINE"
154+
155+freshLine :: String
156+freshLine = "FRESH_LINE"
157+
158+whitespace :: String
159+whitespace = "WHITESPACE"
160+
161+keyword :: String -> String
162+keyword kwd =
163+ "KEYWORD=" ++ kwd
164+
165+unkeyword :: String -> Maybe String
166+unkeyword message =
167+ if List.isPrefixOf "KEYWORD=" message
168+ then Just (drop (length "KEYWORD=") message)
169+ else Nothing
170+
171+
172+-- REPORTING PARSE ERRORS
173+
174+parseErrorReport :: [Parsec.Message] -> Report.Report
175+parseErrorReport messages =
120176 let
121− s = if null tvars then "" else "s"
177+ addMsg message hint =
178+ case message of
179+ Parsec.SysUnExpect _msg ->
180+ hint
181+
182+ Parsec.UnExpect _msg ->
183+ hint
184+
185+ Parsec.Expect msg ->
186+ let
187+ msg' =
188+ if msg `elem` [whitespace, newline, freshLine]
189+ then "whitespace"
190+ else msg
191+ in
192+ hint { _expected = Set.insert msg' (_expected hint) }
193+
194+ Parsec.Message msg ->
195+ hint { _messages = msg : _messages hint }
196+
197+ (ParseHint msgs expects) =
198+ foldr addMsg emptyHint messages
199+
200+ preHint =
201+ case msgs of
202+ [msg] ->
203+ case unkeyword msg of
204+ Just kwd ->
205+ "It looks like the keyword `" ++ kwd ++ "` is being used as a variable.\n"
206+ ++ "Try renaming it to something else."
207+ Nothing ->
208+ msg
209+
210+ _ -> "I ran into something unexpected when parsing your code!"
211+
212+ postHint =
213+ if Set.null expects
214+ then ""
215+ else
216+ "I am looking for one of the following things:\n"
217+ ++ concatMap ("\n "++) (Set.toList expects)
122218 in
123− Report.simple
124− ("Type '" ++ typeName ++ "' uses unbound type variable" ++ s ++ ": "
125− ++ List.intercalate ", " (tvar:tvars)
126− )
127− ("All type variables must be listed to avoid sneaky type errors.\n"
128− ++ "Imagine one '" ++ typeName ++ "' where '" ++ tvar ++ "' is an Int and\n"
129− ++ "another where it is a Bool. They both look like a '" ++ typeName ++ "'\n"
130− ++ "to the type checker, but they are actually different types!\n\n"
131− ++ "Maybe you want a definition like this?\n"
132− ++ concatMap ("\n "++) (lines revisedDeclaration)
133− )
134−
135−
136−parseErrorHint :: Parsec.Message -> String
137−parseErrorHint message =
138− case message of
139− Parsec.SysUnExpect msg ->
140− "SysUnExpect: " ++ msg
141−
142− Parsec.UnExpect msg ->
143− "UnExpect: " ++ msg
144−
145− Parsec.Expect msg ->
146− "UnExpect: " ++ msg
147−
148− Parsec.Message msg ->
149− "UnExpect: " ++ msg
219+ Report.simple "SYNTAX PROBLEM" preHint postHint
220+
221+
222+data ParseHint = ParseHint
223+ { _messages :: [String]
224+ , _expected :: Set.Set String
225+ }
226+ deriving (Show)
227+
228+
229+emptyHint :: ParseHint
230+emptyHint =
231+ ParseHint [] Set.empty
src/Reporting/Error/Type.hs+241−54
@@ -1,9 +1,13 @@
11{-# OPTIONS_GHC -Wall #-}
22module Reporting.Error.Type where
33
4+import qualified Data.Char as Char
5+import Text.PrettyPrint ((<+>))
46import qualified Text.PrettyPrint as P
57
6−import qualified AST.Type as T
8+import qualified AST.Helpers as Help
9+import qualified AST.Type as Type
10+import qualified AST.Variable as Var
711import qualified Reporting.PrettyPrint as P
812import qualified Reporting.Region as Region
913import qualified Reporting.Report as Report
@@ -11,113 +15,296 @@ import qualified Reporting.Report as Report
1115
1216data Error
1317 = Mismatch Mismatch
14− | InfiniteType String T.Canonical
15− | BadMain T.Canonical
18+ | InfiniteType InfiniteType
19+ | BadMain Type.Canonical
1620
1721
1822data Mismatch = MismatchInfo
1923 { _hint :: Hint
20− , _leftType :: T.Canonical
21− , _rightType :: T.Canonical
22− , _note :: Note
24+ , _leftType :: Type.Canonical
25+ , _rightType :: Type.Canonical
26+ , _note :: Maybe String
27+ }
28+
29+
30+data InfiniteType = InfiniteTypeInfo
31+ { _name :: String
32+ , _var :: Type.Canonical
33+ , _type :: Type.Canonical
2334 }
2435
2536
2637data Hint
27− = None
28− | CaseBranch Int Region.Region
38+ = CaseBranch Int Region.Region
2939 | Case
40+ | IfCondition
3041 | IfBranches
3142 | MultiIfBranch Int Region.Region
3243 | If
44+ | List
45+ | ListElement Int Region.Region
46+ | BinopLeft Var.Canonical Region.Region
47+ | BinopRight Var.Canonical Region.Region
48+ | Binop Var.Canonical
49+ | Function (Maybe Var.Canonical)
50+ | UnexpectedArg (Maybe Var.Canonical) Int Region.Region
51+ | FunctionArity (Maybe Var.Canonical) Int Int Region.Region
52+ | BadTypeAnnotation String
53+ | Instance String
54+ | Literal String
55+ | Pattern Pattern
56+ | Shader
57+ | Range
58+ | Lambda
59+ | Record
3360
3461
35−data Note
36− = PreNote String
37− | NoNote
38− | PostNote String
62+data Pattern
63+ = PVar String
64+ | PAlias String
65+ | PData String
66+ | PRecord
3967
4068
4169-- TO REPORT
4270
43−toReport :: Error -> Report.Report
44−toReport err =
71+toReport :: P.Dealiaser -> Error -> Report.Report
72+toReport dealiaser err =
4573 case err of
4674 Mismatch (MismatchInfo hint leftType rightType note) ->
4775 let
48− (subRegion, preHint) = hintToString hint
76+ (subRegion, preHint, maybePostHint) = hintToString hint
4977
5078 postHint =
51− "To be more specific, as I infer all the types, I am seeing a conflict between\n"
52− ++ "this type:\n\n"
53− ++ P.render (P.nest 4 (P.pretty False leftType))
54− ++ "\n\nand this type:\n\n"
55− ++ P.render (P.nest 4 (P.pretty False rightType))
79+ maybe "" (++"\n\n") note
80+ ++ maybe "" (++"\n\n") maybePostHint
81+ ++ "As I infer the types of values flowing through your program, I see a conflict\n"
82+ ++ "between these two types:\n\n"
83+ ++ P.render (P.nest 4 (P.pretty dealiaser False leftType))
84+ ++ "\n\n"
85+ ++ P.render (P.nest 4 (P.pretty dealiaser False rightType))
5686 in
57− case note of
58− PreNote msg ->
59− Report.Report subRegion (preHint ++ "\n\n" ++ msg) postHint
60−
61− NoNote ->
62− Report.Report subRegion preHint postHint
63−
64− PostNote msg ->
65− Report.Report subRegion preHint (postHint ++ "\n\n" ++ msg)
66−
87+ Report.Report "TYPE MISMATCH" subRegion preHint postHint
6788
89+ InfiniteType (InfiniteTypeInfo name var tipe) ->
90+ let
91+ prettyVar =
92+ P.pretty dealiaser False var
6893
69− InfiniteType var tipe ->
70− Report.simple "This expression is leading me to infer an infinite type." $
71− "Maybe you are trying to do some tricky recursion? Try breaking the expression\n"
72− ++ "into smaller pieces. Give each piece a name and try to write down its type.\n\n"
73− ++ "Type inference got stuck when type '" ++ var ++ "' needed equal to:\n\n"
74− ++ P.render (P.nest 4 (P.pretty False tipe))
75− ++ "\n\nNotice that type variable '" ++ var ++ "' appears there too, so if we\n"
76− ++ "expanded this type, it would just keep getting bigger and bigger."
94+ prettyType =
95+ P.pretty dealiaser False tipe
96+ in
97+ Report.simple "INFINITE TYPE"
98+ ( "I am inferring weird self-referential type for `" ++ name ++ "`"
99+ )
100+ ( "The bit of the type that is self-referential looks like this:\n\n"
101+ ++ P.render (P.nest 4 (prettyVar <+> P.equals <+> prettyType))
102+ ++ "\n\nThe cause is often that the usage of `" ++ name ++ "` is flipped around.\n\n"
103+ ++ "Maybe you are inserting a data structure into an element? Maybe you are giving\n"
104+ ++ "a function to an argument? Either way, something is probably backwards!\n\n"
105+ ++ "Try breaking the code related to `" ++ name ++ "` into smaller pieces.\n"
106+ ++ "Give each piece a name and try to write down its type."
107+ )
77108
78109 BadMain tipe ->
79− Report.simple "The 'main' value has an unsupported type." $
110+ Report.simple "BAD MAIN TYPE" "The 'main' value has an unsupported type." $
80111 "I need an Element, Html, (Signal Element), or (Signal Html) so I can render it\n"
81112 ++ "on screen, but you gave me:\n\n"
82− ++ P.render (P.nest 4 (P.pretty False tipe))
113+ ++ P.render (P.nest 4 (P.pretty dealiaser False tipe))
83114
84115
85−hintToString :: Hint -> (Maybe Region.Region, String)
116+hintToString :: Hint -> (Maybe Region.Region, String, Maybe String)
86117hintToString hint =
87118 case hint of
88− None ->
89− ( Nothing
90− , "This expression is triggering a type mismatch."
91− )
92−
93119 CaseBranch branchNumber region ->
94120 ( Just region
95− , "The branches of this case-expression return different types of values.\n\n"
96− ++ "I noticed the mismatch in branch #" ++ show branchNumber ++ ", but go through and make sure every\n"
97− ++ "branch returns the same type of value."
121+ , "The " ++ ordinalize branchNumber ++ " branch of this `case` results in an unexpected type of value."
122+ , Just "All branches should match so that no matter which one we take, we always get\nback the same type of value."
98123 )
99124
100125 Case ->
101126 ( Nothing
102127 , "All the branches of this case-expression are consistent, but the overall\n"
103128 ++ "type does not match how it is used elsewhere."
129+ , Nothing
130+ )
131+
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."
104136 )
105137
106138 IfBranches ->
107139 ( Nothing
108− , "The branches of this if-expression return different types of values.\n"
109− ++ "All branches must have the same return type!"
140+ , "The branches of this `if` result in different types of values."
141+ , Just "All branches should match so that no matter which one we take, we always get\nback the same type of value."
110142 )
111143
112144 MultiIfBranch branchNumber region ->
113145 ( Just region
114− , "The branches of this if-expression return different types of values.\n\n"
115− ++ "I noticed the mismatch in branch #" ++ show branchNumber ++ ", but go through and make sure every\n"
116− ++ "branch returns the same type of value."
146+ , "The " ++ ordinalize branchNumber ++ " branch of this `if` results in an unexpected type of value."
147+ , Just "All branches should match so that no matter which one we take, we always get\nback the same type of value."
117148 )
118149
119150 If ->
120151 ( Nothing
121152 , "All the branches of this if-expression are consistent, but the overall\n"
122153 ++ "type does not match how it is used elsewhere."
154+ , Nothing
155+ )
156+
157+ ListElement elementNumber region ->
158+ ( Just region
159+ , "The " ++ ordinalize elementNumber ++ " element of this list is an unexpected type of value."
160+ , Just "All elements should be the same type of value so that we can iterate over the\nlist without running into unexpected values."
161+ )
162+
163+ List ->
164+ ( Nothing
165+ , "All the elements in this list are the same type, but the overall\n"
166+ ++ "type does not match how it is used elsewhere."
167+ , Nothing
168+ )
169+
170+ BinopLeft op region ->
171+ ( Just region
172+ , "The left argument of " ++ prettyOperator op ++ " is causing a type mismatch."
173+ , Nothing
174+ )
175+
176+ BinopRight op region ->
177+ ( Just region
178+ , "The right argument of " ++ prettyOperator op ++ " is causing a type mismatch."
179+ , Nothing
180+ )
181+
182+ Binop op ->
183+ ( Nothing
184+ , "The two arguments to " ++ prettyOperator op ++ " are fine, but the overall type of this expression\n"
185+ ++ "does not match how it is used elsewhere."
186+ , Nothing
187+ )
188+
189+ Function maybeName ->
190+ ( Nothing
191+ , "The return type of " ++ funcName maybeName ++ " is being used in unexpected ways."
192+ , Nothing
193+ )
194+
195+ UnexpectedArg maybeName index region ->
196+ ( Just region
197+ , "The " ++ ordinalize index ++ " argument to " ++ funcName maybeName
198+ ++ " has an unexpected type."
199+ , Nothing
200+ )
201+
202+ FunctionArity maybeName expected actual region ->
203+ let
204+ s = if expected <= 1 then "" else "s"
205+ in
206+ ( Just region
207+ , capitalize (funcName maybeName) ++ " is expecting " ++ show expected
208+ ++ " argument" ++ s ++ ", but was given " ++ show actual ++ "."
209+ , Nothing
210+ )
211+
212+ BadTypeAnnotation name ->
213+ ( Nothing
214+ , "The type annotation for `" ++ name ++ "` does not match its definition."
215+ , Nothing
123216 )
217+
218+ Instance name ->
219+ ( Nothing
220+ , "Given how `" ++ name ++ "` is defined, this use will not work out."
221+ , Nothing
222+ )
223+
224+ Literal name ->
225+ ( Nothing
226+ , "This " ++ name ++ " value is being used as if it is some other type of value."
227+ , Nothing
228+ )
229+
230+ Pattern patErr ->
231+ let
232+ thing =
233+ case patErr of
234+ PVar name -> "variable `" ++ name ++ "`"
235+ PAlias name -> "alias `" ++ name ++ "`"
236+ PData name -> "`" ++ name ++ "`"
237+ PRecord -> "a record"
238+ in
239+ ( Nothing
240+ , "Problem with " ++ thing ++ " in this pattern match."
241+ , Nothing
242+ )
243+
244+ Shader ->
245+ ( Nothing
246+ , "There is some problem with this GLSL shader."
247+ , Nothing
248+ )
249+
250+ Range ->
251+ ( Nothing
252+ , "The low and high members of this list range are not the same type of value."
253+ , Nothing
254+ )
255+
256+ Lambda ->
257+ ( Nothing
258+ , "This anonymous function is being used in an unexpected way."
259+ , Nothing
260+ )
261+
262+ Record ->
263+ ( Nothing
264+ , "This record is being used in an unexpected way."
265+ , Nothing
266+ )
267+
268+
269+prettyOperator :: Var.Canonical -> String
270+prettyOperator (Var.Canonical _ opName) =
271+ if Help.isOp opName
272+ then "(" ++ opName ++ ")"
273+ else "`" ++ opName ++ "`"
274+
275+
276+funcName :: Maybe Var.Canonical -> String
277+funcName maybeVar =
278+ case maybeVar of
279+ Nothing ->
280+ "this function"
281+
282+ Just var ->
283+ "function " ++ prettyOperator var
284+
285+
286+capitalize :: String -> String
287+capitalize string =
288+ case string of
289+ [] -> []
290+ c : cs ->
291+ Char.toUpper c : cs
292+
293+
294+ordinalize :: Int -> String
295+ordinalize number =
296+ let
297+ remainder10 =
298+ number `mod` 10
299+
300+ remainder100 =
301+ number `mod` 100
302+
303+ ending
304+ | remainder100 `elem` [11..13] = "th"
305+ | remainder10 == 1 = "st"
306+ | remainder10 == 2 = "nd"
307+ | remainder10 == 3 = "rd"
308+ | otherwise = "th"
309+ in
310+ show number ++ ending
src/Reporting/PrettyPrint.hs+14−2
@@ -1,19 +1,31 @@
11{-# LANGUAGE FlexibleInstances #-}
22module Reporting.PrettyPrint where
33
4+import qualified Data.Map as Map
45import Text.PrettyPrint as P
6+
57import qualified AST.Helpers as Help
68
79
10+-- PRETTY
11+
812class Pretty a where
9− pretty :: Bool -> a -> Doc
13+ pretty :: Dealiaser -> Bool -> a -> Doc
14+
1015
16+type Dealiaser =
17+ Map.Map String String
18+
19+
20+-- INSTANCES
1121
1222instance Pretty String where
13− pretty _ str =
23+ pretty _ _ str =
1424 P.text str
1525
1626
27+-- HELPERS
28+
1729commaCat :: [Doc] -> Doc
1830commaCat docs =
1931 cat (punctuate comma docs)
src/Reporting/Report.hs+180−52
@@ -1,41 +1,127 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE OverloadedStrings #-}
13module Reporting.Report
24 ( Report(Report)
35 , simple
46 , toString
7+ , toJson
8+ , printError, printWarning
59 ) where
610
7−import Elm.Utils ((|>))
11+import Control.Applicative ((<|>))
12+import Control.Monad.Writer (Writer, execWriter, tell)
13+import Data.Aeson ((.=))
14+import qualified Data.Aeson.Types as Json
15+import System.Console.ANSI
16+import System.IO (hPutStr, stderr)
17+
818import qualified Reporting.Region as R
919
1020
1121data Report = Report
12− { highlight :: Maybe R.Region
13− , preHint :: String
14− , postHint :: String
22+ { _title :: String
23+ , _highlight :: Maybe R.Region
24+ , _preHint :: String
25+ , _postHint :: String
26+ }
27+
28+
29+simple :: String -> String -> String -> Report
30+simple title pre post =
31+ Report title Nothing pre post
32+
33+
34+toString :: String -> R.Region -> Report -> String -> String
35+toString location region report source =
36+ execWriter (render plain location region report source)
37+
38+
39+printError :: String -> R.Region -> Report -> String -> IO ()
40+printError location region report source =
41+ render (ansi Error) location region report source
42+
43+
44+printWarning :: String -> R.Region -> Report -> String -> IO ()
45+printWarning location region report source =
46+ render (ansi Warning) location region report source
47+
48+
49+render
50+ :: (Monad m)
51+ => Renderer m
52+ -> String
53+ -> R.Region
54+ -> Report
55+ -> String
56+ -> m ()
57+render renderer location region (Report title highlight pre post) source =
58+ do messageBar renderer title location
59+ normal renderer (pre ++ "\n\n")
60+ grabRegion renderer highlight region source
61+ normal renderer ("\n" ++ if null post then "\n" else post ++ "\n\n\n")
62+
63+
64+toJson :: [Json.Pair] -> Report -> (Maybe R.Region, [Json.Pair])
65+toJson extraFields (Report title subregion pre post) =
66+ let
67+ fields =
68+ [ "tag" .= title
69+ , "overview" .= pre
70+ , "details" .= post
71+ ]
72+ in
73+ (subregion, fields ++ extraFields)
74+
75+
76+-- RENDERING
77+
78+data Renderer m = Renderer
79+ { normal :: String -> m ()
80+ , header :: String -> m ()
81+ , accent :: String -> m ()
1582 }
1683
1784
18−simple :: String -> String -> Report
19−simple pre post =
20− Report Nothing pre post
85+plain :: Renderer (Writer String)
86+plain =
87+ Renderer tell tell tell
88+
89+
90+data Type = Error | Warning
91+
92+
93+ansi :: Type -> Renderer IO
94+ansi tipe =
95+ let
96+ put =
97+ hPutStr stderr
98+
99+ put' intensity color string =
100+ do hSetSGR stderr [SetColor Foreground intensity color]
101+ put string
102+ hSetSGR stderr [Reset]
103+
104+ accentColor =
105+ case tipe of
106+ Error -> Red
107+ Warning -> Yellow
108+ in
109+ Renderer
110+ put
111+ (put' Dull Cyan)
112+ (put' Dull accentColor)
21113
22114
23−toString :: String -> String -> R.Region -> Report -> String -> String
24−toString tag location region report source =
25− concat
26− [ messageBar tag location
27− , preHint report ++ "\n\n"
28− , grabRegion (highlight report) region source ++ "\n"
29− , postHint report ++ "\n\n\n"
30− ]
31115
32116
33117-- REPORT HEADER
34118
35−messageBar :: String -> String -> String
36−messageBar tag location =
37− let usedSpace = 4 + length tag + 1 + length location
119+messageBar :: Renderer m -> String -> String -> m ()
120+messageBar renderer tag location =
121+ let
122+ usedSpace = 4 + length tag + 1 + length location
38123 in
124+ header renderer $
39125 "-- " ++ tag ++ " "
40126 ++ replicate (max 1 (80 - usedSpace)) '-'
41127 ++ " " ++ location ++ "\n\n"
@@ -43,12 +129,20 @@ messageBar tag location =
43129
44130-- REGIONS
45131
46−grabRegion :: Maybe R.Region -> R.Region -> String -> String
47−grabRegion maybeSubRegion (R.Region start end) source =
132+grabRegion
133+ :: (Monad m)
134+ => Renderer m
135+ -> Maybe R.Region
136+ -> R.Region
137+ -> String
138+ -> m ()
139+grabRegion renderer maybeSubRegion region@(R.Region start end) source =
48140 let
49141 (R.Position startLine startColumn) = start
50142 (R.Position endLine endColumn) = end
51143
144+ (|>) = flip ($)
145+
52146 relevantLines =
53147 lines source
54148 |> drop (startLine - 1)
@@ -56,23 +150,16 @@ grabRegion maybeSubRegion (R.Region start end) source =
56150 in
57151 case relevantLines of
58152 [] ->
59− error "something has gone badly wrong with reporting source locations"
153+ normal renderer ""
60154
61− [theLine] ->
62− let
63− n = startLine
64− w = length (show (n + 1))
65− underline =
66− replicate (startColumn - 1) ' '
67− ++ replicate (max 1 (endColumn - startColumn)) '^'
68− number =
69− addLineNumber Nothing w
70− in
71− unlines
72− [ number (n-1) ""
73− , number n theLine
74− , number (n+1) underline
75− ]
155+ [sourceLine] ->
156+ singleLineRegion renderer startLine sourceLine $
157+ case maybeSubRegion of
158+ Nothing ->
159+ (0, startColumn, endColumn, length sourceLine)
160+
161+ Just (R.Region s e) ->
162+ (startColumn, R.column s, R.column e, endColumn)
76163
77164 firstLine : rest ->
78165 let
@@ -87,28 +174,69 @@ grabRegion maybeSubRegion (R.Region start end) source =
87174 filteredFirstLine : init rest ++ [filteredLastLine]
88175
89176 lineNumbersWidth =
90− length (show (endLine + 1))
177+ length (show endLine)
178+
179+ subregion =
180+ maybeSubRegion <|> Just region
91181
92182 numberedLines =
93183 zipWith
94− (addLineNumber maybeSubRegion lineNumbersWidth)
95− [startLine - 1 .. endLine + 1]
96− ("" : focusedRelevantLines ++ [""])
184+ (addLineNumber renderer subregion lineNumbersWidth)
185+ [startLine .. endLine]
186+ focusedRelevantLines
97187 in
98− unlines numberedLines
99−
100−
101−addLineNumber :: Maybe R.Region -> Int -> Int -> String -> String
102−addLineNumber maybeSubRegion width n line =
188+ mapM_ (\line -> line >> normal renderer "\n") numberedLines
189+
190+
191+addLineNumber
192+ :: (Monad m)
193+ => Renderer m
194+ -> Maybe R.Region
195+ -> Int
196+ -> Int
197+ -> String
198+ -> m ()
199+addLineNumber renderer maybeSubRegion width n line =
103200 let
104201 number =
105− if n < 0 then " " else show n
202+ if n < 0 then " " else show n
203+
204+ lineNumber =
205+ replicate (width - length number) ' ' ++ number ++ "|"
106206
107207 spacer (R.Region start end) =
108− if R.line start <= n && n <= R.line end
109− then ">"
110− else " "
208+ if R.line start <= n && n <= R.line end
209+ then accent renderer ">"
210+ else normal renderer " "
211+ in
212+ do normal renderer lineNumber
213+ maybe (normal renderer " ") spacer maybeSubRegion
214+ normal renderer line
215+
216+
217+singleLineRegion
218+ :: (Monad m)
219+ => Renderer m
220+ -> Int
221+ -> String
222+ -> (Int, Int, Int, Int)
223+ -> m ()
224+singleLineRegion renderer lineNum sourceLine (start, innerStart, innerEnd, end) =
225+ let
226+ width =
227+ length (show lineNum)
228+
229+ underline =
230+ replicate (innerStart + width + 1) ' '
231+ ++ replicate (max 1 (innerEnd - innerStart)) '^'
232+
233+ (|>) = flip ($)
234+
235+ trimmedSourceLine =
236+ sourceLine
237+ |> drop (start - 1)
238+ |> take (end - start + 1)
239+ |> (++) (replicate (start - 1) ' ')
111240 in
112− replicate (width - length number) ' ' ++ number
113− ++ "|" ++ maybe " " spacer maybeSubRegion
114− ++ line
241+ do addLineNumber renderer Nothing width lineNum trimmedSourceLine
242+ accent renderer $ "\n" ++ underline
src/Reporting/Result.hs+41−15
@@ -1,17 +1,21 @@
11module Reporting.Result where
22
3−import qualified Control.Applicative as A
3+import Control.Applicative (Applicative, (<*>), (<|>), pure)
44import qualified Control.Monad as M
55import Control.Monad.Except (Except, runExcept)
66
77import qualified Reporting.Annotation as A
88import qualified Reporting.Region as R
9+import qualified Reporting.PrettyPrint as P
910
1011
1112-- TASK
1213
1314data Result warning error result =
14− Result [A.Located warning] (RawResult [A.Located error] result)
15+ Result
16+ (Maybe P.Dealiaser, [A.Located warning])
17+ (RawResult [A.Located error] result)
18+
1519
1620data RawResult e a
1721 = Ok a
@@ -22,17 +26,17 @@ data RawResult e a
2226
2327ok :: a -> Result w e a
2428ok value =
25− Result [] (Ok value)
29+ Result (Nothing, []) (Ok value)
2630
2731
2832throw :: R.Region -> e -> Result w e a
2933throw region err =
30− Result [] (Err [A.A region err])
34+ Result (Nothing, []) (Err [A.A region err])
3135
3236
3337throwMany :: [A.Located e] -> Result w e a
3438throwMany errors =
35− Result [] (Err errors)
39+ Result (Nothing, []) (Err errors)
3640
3741
3842from :: (e -> e') -> Except [A.Located e] a -> Result w e' a
@@ -58,12 +62,27 @@ mapError f (Result warnings rawResult) =
5862
5963warn :: R.Region -> w -> Result w e ()
6064warn region warning =
61− Result [A.A region warning] (Ok ())
65+ Result (Nothing, [A.A region warning]) (Ok ())
6266
6367
6468addWarnings :: [A.Located w] -> Result w e a -> Result w e a
65−addWarnings newWarnings (Result warnings rawResult) =
66− Result (newWarnings ++ warnings) rawResult
69+addWarnings newWarnings (Result (dealiaser, warnings) rawResult) =
70+ Result (dealiaser, newWarnings ++ warnings) rawResult
71+
72+
73+addDealiaser :: P.Dealiaser -> Result w e a -> Result w e a
74+addDealiaser dealiaser (Result (_, warnings) rawResult) =
75+ Result (Just dealiaser, warnings) rawResult
76+
77+
78+destruct :: (e -> b) -> (a -> b) -> RawResult e a -> b
79+destruct errFunc okFunc rawResult =
80+ case rawResult of
81+ Ok answer ->
82+ okFunc answer
83+
84+ Err errors ->
85+ errFunc errors
6786
6887
6988-- EXTRA FANCY HELPERS
@@ -78,12 +97,12 @@ instance M.Functor (Result w e) where
7897 Result warnings (Err msgs)
7998
8099
81−instance A.Applicative (Result w e) where
100+instance Applicative (Result w e) where
82101 pure value =
83102 ok value
84103
85− (<*>) (Result warnings resultFunc) (Result warnings' resultVal) =
86− Result (warnings ++ warnings') $
104+ (<*>) (Result context resultFunc) (Result context' resultVal) =
105+ Result (merge context context') $
87106 case (resultFunc, resultVal) of
88107 (Ok func, Ok val) ->
89108 Ok (func val)
@@ -102,12 +121,19 @@ instance M.Monad (Result w e) where
102121 return value =
103122 ok value
104123
105− (>>=) (Result warnings rawResult) callback =
124+ (>>=) (Result context rawResult) callback =
106125 case rawResult of
107126 Err msg ->
108− Result warnings (Err msg)
127+ Result context (Err msg)
109128
110129 Ok value ->
111− let (Result warnings' rawResult') = callback value
130+ let (Result context' rawResult') = callback value
112131 in
113− Result (warnings ++ warnings') rawResult'
132+ Result (merge context context') rawResult'
133+
134+
135+merge :: (Maybe a, [b]) -> (Maybe a, [b]) -> (Maybe a, [b])
136+merge (dealiaser, warnings) (dealiaser', warnings') =
137+ ( dealiaser <|> dealiaser'
138+ , warnings ++ warnings'
139+ )
src/Reporting/Warning.hs+56−10
@@ -1,27 +1,73 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE OverloadedStrings #-}
13module Reporting.Warning where
24
3−import qualified AST.Module as Module
5+import Data.Aeson ((.=))
6+import qualified Data.Aeson as Json
7+import qualified Text.PrettyPrint as P
8+import Text.PrettyPrint ((<+>))
49
10+import qualified AST.Module as Module
11+import qualified AST.Type as Type
512import qualified Reporting.Annotation as A
13+import qualified Reporting.PrettyPrint as P
14+import qualified Reporting.Report as Report
15+
616
17+-- ALL POSSIBLE WARNINGS
718
819data Warning
920 = UnusedImport Module.Name
10− | MissingTypeAnnotation String
21+ | MissingTypeAnnotation String Type.Canonical
1122
1223
1324-- TO STRING
1425
15−toString :: A.Located Warning -> String
16−toString (A.A region warning) =
17− case warning of
18− _ -> error "Warning.toString" region
26+toString :: P.Dealiaser -> String -> String -> A.Located Warning -> String
27+toString dealiaser location source (A.A region warning) =
28+ Report.toString location region (toReport dealiaser warning) source
1929
2030
21−-- TO JSON
31+print :: P.Dealiaser -> String -> String -> A.Located Warning -> IO ()
32+print dealiaser location source (A.A region warning) =
33+ Report.printWarning location region (toReport dealiaser warning) source
2234
23−toJson :: A.Located Warning -> String
24−toJson (A.A region warning) =
35+
36+toReport :: P.Dealiaser -> Warning -> Report.Report
37+toReport dealiaser warning =
2538 case warning of
26− _ -> error "Warning.toJson" region
39+ UnusedImport moduleName ->
40+ Report.simple
41+ "unused import"
42+ ("Module `" ++ Module.nameToString moduleName ++ "` is unused.")
43+ ""
44+
45+ MissingTypeAnnotation name inferredType ->
46+ Report.simple
47+ "missing type annotation"
48+ ("Top-level value `" ++ name ++ "` does not have a type annotation.")
49+ ( "The type annotation you want looks something like this:\n\n"
50+ ++ P.render (P.nest 4 typeDoc)
51+ )
52+ where
53+ typeDoc =
54+ P.hang
55+ (P.text name <+> P.colon)
56+ 4
57+ (P.pretty dealiaser False inferredType)
58+
59+
60+-- TO JSON
2761
62+toJson :: P.Dealiaser -> FilePath -> A.Located Warning -> Json.Value
63+toJson dealiaser filePath (A.A region warning) =
64+ let
65+ (maybeRegion, additionalFields) =
66+ Report.toJson [] (toReport dealiaser warning)
67+ in
68+ Json.object $
69+ [ "file" .= filePath
70+ , "region" .= maybe region id maybeRegion
71+ , "type" .= ("warning" :: String)
72+ ]
73+ ++ additionalFields
src/Reporting/Warning/Nitpick.hsdeleted−14
@@ -1,14 +0,0 @@
1−module Reporting.Warning.Nitpick where
2−
3−import qualified AST.Pattern as Pattern
4−import qualified AST.Literal as Literal
5−import qualified Data.Set as Set
6−import qualified AST.Variable as Var
7−
8−
9−data Warning
10− = Redundant Pattern.CanonicalPattern
11− -- The argument is the list of patterns that would have to
12− -- be added for the case expression to be exhaustive.
13− | Inexhaustive [CanonicalPat]
14−
src/Type/Constrain/Expression.hs+270−85
@@ -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
@@ -27,9 +28,8 @@ constrain
2728 -> Canonical.Expr
2829 -> Type
2930 -> IO TypeConstraint
30−constrain env (A.A region expression) tipe =
31+constrain env annotatedExpr@(A.A region expression) tipe =
3132 let list t = Env.get env Env.types "List" <| t
32− (===) = CEqual Error.None region
3333 (<?) = CInstance region
3434 in
3535 case expression of
@@ -57,7 +57,7 @@ constrain env (A.A region expression) tipe =
5757 uniform = makeRec Lit.uniform unif
5858 varying = makeRec Lit.varying (termN EmptyRecord1)
5959 in
60− return (tipe === shaderTipe attribute uniform varying)
60+ return (CEqual Error.Shader region tipe (shaderTipe attribute uniform varying))
6161
6262 E.Var var ->
6363 let name = V.toString var
@@ -68,23 +68,17 @@ constrain env (A.A region expression) tipe =
6868 existsNumber $ \n ->
6969 do lowCon <- constrain env lowExpr n
7070 highCon <- constrain env highExpr n
71− return $ CAnd [lowCon, highCon, list n === tipe]
71+ return $ CAnd
72+ [ lowCon
73+ , highCon
74+ , CEqual Error.Range region (list n) tipe
75+ ]
7276
7377 E.ExplicitList exprs ->
74− exists $ \x ->
75− do constraints <- mapM (\e -> constrain env e x) exprs
76− return $ CAnd (list x === tipe : constraints)
78+ constrainList env region exprs tipe
7779
7880 E.Binop op leftExpr rightExpr ->
79− exists $ \leftType ->
80− exists $ \rightType ->
81− do leftCon <- constrain env leftExpr leftType
82− rightCon <- constrain env rightExpr rightType
83− return $ CAnd
84− [ leftCon
85− , rightCon
86− , V.toString op <? (leftType ==> rightType ==> tipe)
87− ]
81+ constrainBinop env region op leftExpr rightExpr tipe
8882
8983 E.Lambda pattern body ->
9084 exists $ \argType ->
@@ -96,16 +90,16 @@ constrain env (A.A region expression) tipe =
9690 (CLet [monoscheme (Fragment.typeEnv fragment)]
9791 (Fragment.typeConstraint fragment /\ bodyCon)
9892 )
99− return $ con /\ tipe === (argType ==> resType)
93+ return $ con /\ CEqual Error.Lambda region tipe (argType ==> resType)
10094
101− E.App func arg ->
102− exists $ \t ->
103− do funcCon <- constrain env func (t ==> tipe)
104− argCon <- constrain env arg t
105− return (funcCon /\ argCon)
95+ E.App _ _ ->
96+ let
97+ (f:args) = E.collectApps annotatedExpr
98+ in
99+ constrainApp env region f args tipe
106100
107− E.MultiIf branches ->
108− constrainIf env region branches tipe
101+ E.MultiIf branches finally ->
102+ constrainIf env region branches finally tipe
109103
110104 E.Case expr branches ->
111105 constrainCase env region expr branches tipe
@@ -135,7 +129,11 @@ constrain env (A.A region expression) tipe =
135129 recordCon <- constrain env expr recordType
136130 let newRecordType =
137131 record (Map.singleton label [valueType]) recordType
138− return (CAnd [valueCon, recordCon, tipe === newRecordType])
132+ return $ CAnd
133+ [ valueCon
134+ , recordCon
135+ , CEqual Error.Record region tipe newRecordType
136+ ]
139137
140138 E.Modify expr fields ->
141139 exists $ \t ->
@@ -145,7 +143,7 @@ constrain env (A.A region expression) tipe =
145143
146144 newVars <- Monad.forM fields $ \_ -> variable Flexible
147145 let newFields = ST.fieldMap (zip (map fst fields) (map varN newVars))
148− let cNew = tipe === record newFields t
146+ let cNew = CEqual Error.Record region tipe (record newFields t)
149147
150148 cs <- Monad.zipWithM (constrain env) (map snd fields) (map varN newVars)
151149
@@ -160,7 +158,7 @@ constrain env (A.A region expression) tipe =
160158 (map varN vars)
161159 let fields' = ST.fieldMap (zip (map fst fields) (map varN vars))
162160 let recordType = record fields' (termN EmptyRecord1)
163− return (ex vars (CAnd (fieldCons ++ [tipe === recordType])))
161+ return (ex vars (CAnd (fieldCons ++ [CEqual Error.Record region tipe recordType])))
164162
165163 E.Let defs body ->
166164 do bodyCon <- constrain env body tipe
@@ -171,7 +169,8 @@ constrain env (A.A region expression) tipe =
171169 (Info [] [] [] Map.empty CTrue CTrue)
172170 (concatMap expandPattern defs)
173171
174− let letScheme = [ Scheme rqs fqs (CLet [monoscheme headers] c2) headers ]
172+ let letScheme =
173+ [ Scheme rqs fqs (CLet [monoscheme headers] c2) headers ]
175174
176175 return $ CLet schemes (CLet letScheme (c1 /\ bodyCon))
177176
@@ -186,6 +185,151 @@ constrain env (A.A region expression) tipe =
186185 E.Task _ expr _ ->
187186 constrain env expr tipe
188187
188+ E.Crash _ ->
189+ return CTrue
190+
191+
192+-- CONSTRAIN APP
193+
194+constrainApp
195+ :: Env.Environment
196+ -> R.Region
197+ -> Canonical.Expr
198+ -> [Canonical.Expr]
199+ -> Type
200+ -> IO TypeConstraint
201+constrainApp env region f args tipe =
202+ do funcVar <- variable Flexible
203+ funcCon <- constrain env f (varN funcVar)
204+
205+ (vars, argCons, numberOfArgsCons, argMatchCons, _, returnVar) <-
206+ argConstraints env maybeName region (length args) funcVar 1 args
207+
208+ let returnCon =
209+ CEqual (Error.Function maybeName) region (varN returnVar) tipe
210+
211+ return $ ex (funcVar : vars) $
212+ CAnd (funcCon : argCons ++ numberOfArgsCons ++ argMatchCons ++ [returnCon])
213+ where
214+ maybeName =
215+ case f of
216+ A.A _ (E.Var canonicalName) ->
217+ Just canonicalName
218+
219+ _ ->
220+ Nothing
221+
222+
223+argConstraints
224+ :: Env.Environment
225+ -> Maybe V.Canonical
226+ -> R.Region
227+ -> Int
228+ -> Variable
229+ -> Int
230+ -> [Canonical.Expr]
231+ -> IO ([Variable], [TypeConstraint], [TypeConstraint], [TypeConstraint], Maybe R.Region, Variable)
232+argConstraints env name region totalArgs overallVar index args =
233+ case args of
234+ [] ->
235+ return ([], [], [], [], Nothing, overallVar)
236+
237+ expr@(A.A subregion _) : rest ->
238+ do argVar <- variable Flexible
239+ argCon <- constrain env expr (varN argVar)
240+ argIndexVar <- variable Flexible
241+ localReturnVar <- variable Flexible
242+
243+ (vars, argConRest, numberOfArgsRest, argMatchRest, restRegion, returnVar) <-
244+ argConstraints env name region totalArgs localReturnVar (index + 1) rest
245+
246+ let arityRegion =
247+ maybe subregion (R.merge subregion) restRegion
248+
249+ let numberOfArgsCon =
250+ CEqual
251+ (Error.FunctionArity name (index - 1) totalArgs arityRegion)
252+ region
253+ (varN argIndexVar ==> varN localReturnVar)
254+ (varN overallVar)
255+
256+ let argMatchCon =
257+ CEqual
258+ (Error.UnexpectedArg name index subregion)
259+ region
260+ (varN argIndexVar)
261+ (varN argVar)
262+
263+ return
264+ ( argVar : argIndexVar : localReturnVar : vars
265+ , argCon : argConRest
266+ , numberOfArgsCon : numberOfArgsRest
267+ , argMatchCon : argMatchRest
268+ , Just arityRegion
269+ , returnVar
270+ )
271+
272+
273+
274+
275+-- CONSTRAIN BINOP
276+
277+constrainBinop
278+ :: Env.Environment
279+ -> R.Region
280+ -> V.Canonical
281+ -> Canonical.Expr
282+ -> Canonical.Expr
283+ -> Type
284+ -> IO TypeConstraint
285+constrainBinop env region op leftExpr@(A.A leftRegion _) rightExpr@(A.A rightRegion _) tipe =
286+ do leftVar <- variable Flexible
287+ rightVar <- variable Flexible
288+
289+ leftCon <- constrain env leftExpr (varN leftVar)
290+ rightCon <- constrain env rightExpr (varN rightVar)
291+
292+ leftVar' <- variable Flexible
293+ rightVar' <- variable Flexible
294+ answerVar <- variable Flexible
295+
296+ let opType = varN leftVar' ==> varN rightVar' ==> varN answerVar
297+
298+ return $
299+ ex [leftVar,rightVar,leftVar',rightVar',answerVar] $ CAnd $
300+ [ leftCon
301+ , rightCon
302+ , CInstance region (V.toString op) opType
303+ , CEqual (Error.BinopLeft op leftRegion) region (varN leftVar') (varN leftVar)
304+ , CEqual (Error.BinopRight op rightRegion) region (varN rightVar') (varN rightVar)
305+ , CEqual (Error.Binop op) region tipe (varN answerVar)
306+ ]
307+
308+
309+-- CONSTRAIN LISTS
310+
311+constrainList
312+ :: Env.Environment
313+ -> R.Region
314+ -> [Canonical.Expr]
315+ -> Type
316+ -> IO TypeConstraint
317+constrainList env region exprs tipe =
318+ do (exprInfo, exprCons) <-
319+ unzip <$> mapM elementConstraint exprs
320+
321+ (vars, cons) <- pairCons region Error.ListElement varToCon exprInfo
322+
323+ return $ ex vars (CAnd (exprCons ++ cons))
324+ where
325+ elementConstraint expr@(A.A region' _) =
326+ do var <- variable Flexible
327+ con <- constrain env expr (varN var)
328+ return ( (var, region'), con )
329+
330+ varToCon var =
331+ CEqual Error.List region tipe (Env.get env Env.types "List" <| varN var)
332+
189333
190334-- CONSTRAIN IF EXPRESSIONS
191335
@@ -193,51 +337,55 @@ constrainIf
193337 :: Env.Environment
194338 -> R.Region
195339 -> [(Canonical.Expr, Canonical.Expr)]
340+ -> Canonical.Expr
196341 -> Type
197342 -> IO TypeConstraint
198−constrainIf env region branches tipe =
199− do (branchInfo, branchExprCons) <-
200− unzip <$> mapM constrainBranch branches
343+constrainIf env region branches finally tipe =
344+ do let (conditions, branchExprs) =
345+ second (++ [finally]) (unzip branches)
201346
202− return $
203− ex (map fst branchInfo)
204− (CAnd (branchExprCons ++ branchCons branchInfo))
347+ (condVars, condCons) <-
348+ unzip <$> mapM constrainCondition conditions
349+
350+ (branchInfo, branchExprCons) <-
351+ unzip <$> mapM constrainBranch branchExprs
352+
353+ (vars,cons) <- branchCons branchInfo
354+
355+ return $ ex (condVars ++ vars) (CAnd (condCons ++ branchExprCons ++ cons))
205356 where
206− 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 ])
207365
208− constrainBranch (cond, expr@(A.A branchRegion _)) =
366+ constrainBranch expr@(A.A branchRegion _) =
209367 do branchVar <- variable Flexible
210− condCon <- constrain env cond bool
211368 exprCon <- constrain env expr (varN branchVar)
212369 return
213370 ( (branchVar, branchRegion)
214− , condCon /\ exprCon
371+ , exprCon
215372 )
216373
217374 branchCons branchInfo =
218375 case branchInfo of
219376 [(thenVar, _), (elseVar, _)] ->
220− [ CEqual Error.IfBranches region (varN thenVar) (varN elseVar)
221− , CEqual Error.If region tipe (varN thenVar)
222− ]
377+ return
378+ ( [thenVar,elseVar]
379+ , [ CEqual Error.IfBranches region (varN thenVar) (varN elseVar)
380+ , varToCon thenVar
381+ ]
382+ )
223383
224384 _ ->
225− buildBranchCons 2 branchInfo
226−
227− buildBranchCons n branchInfo =
228− case branchInfo of
229− [] ->
230− []
385+ pairCons region Error.MultiIfBranch varToCon branchInfo
231386
232− (var,_) : [] ->
233− [ CEqual Error.If region tipe (varN var) ]
234−
235− (var,_) : (var',region') : rest ->
236− let
237− hint = Error.MultiIfBranch n region'
238− con = CEqual hint region (varN var) (varN var')
239− in
240− con : buildBranchCons (n+1) ((var', region') : rest)
387+ varToCon var =
388+ CEqual Error.If region (varN var) tipe
241389
242390
243391-- CONSTRAIN CASE EXPRESSIONS
@@ -251,15 +399,14 @@ constrainCase
251399 -> IO TypeConstraint
252400constrainCase env region expr branches tipe =
253401 do exprVar <- variable Flexible
254−
255402 exprCon <- constrain env expr (varN exprVar)
256403
257404 (branchInfo, branchExprCons) <-
258405 unzip <$> mapM (branch (varN exprVar)) branches
259406
260− return $
261− ex (exprVar : map fst branchInfo)
262− (CAnd (exprCon : branchExprCons ++ branchCons 2 branchInfo))
407+ (vars, cons) <- pairCons region Error.CaseBranch varToCon branchInfo
408+
409+ return $ ex (exprVar : vars) (CAnd (exprCon : branchExprCons ++ cons))
263410 where
264411 branch patternType (pattern, branchExpr@(A.A branchRegion _)) =
265412 do branchVar <- variable Flexible
@@ -270,20 +417,52 @@ constrainCase env region expr branches tipe =
270417 , CLet [Fragment.toScheme fragment] branchCon
271418 )
272419
273− branchCons n branchInfo =
274− case branchInfo of
275− [] ->
276− []
420+ varToCon var =
421+ CEqual Error.Case region tipe (varN var)
277422
278− (var,_) : [] ->
279− [ CEqual Error.Case region tipe (varN var) ]
280423
281− (var,_) : (var',region') : rest ->
282− let
283− hint = Error.CaseBranch n region'
284− con = CEqual hint region (varN var) (varN var')
285− in
286− con : branchCons (n+1) ((var', region') : rest)
424+-- COLLECT PAIRS
425+
426+data Pair = Pair
427+ { _index :: Int
428+ , _var1 :: Variable
429+ , _var2 :: Variable
430+ , _region :: R.Region
431+ }
432+
433+
434+pairCons
435+ :: R.Region
436+ -> (Int -> R.Region -> Error.Hint)
437+ -> (Variable -> TypeConstraint)
438+ -> [(Variable, R.Region)]
439+ -> IO ([Variable], [TypeConstraint])
440+pairCons region pairHint varToCon items =
441+ let
442+ pairToCon (Pair index var1 var2 subregion) =
443+ CEqual (pairHint index subregion) region (varN var1) (varN var2)
444+ in
445+ case collectPairs 2 items of
446+ Nothing ->
447+ do var <- variable Flexible
448+ return ([var], [varToCon var])
449+
450+ Just (pairs, var) ->
451+ return (map fst items, map pairToCon pairs ++ [varToCon var])
452+
453+
454+collectPairs :: Int -> [(Variable, R.Region)] -> Maybe ([Pair], Variable)
455+collectPairs index items =
456+ case items of
457+ [] ->
458+ Nothing
459+
460+ (var,_) : [] ->
461+ Just ([], var)
462+
463+ (var,_) : rest@((var',region) : _) ->
464+ do (pairs, summaryVar) <- collectPairs (index+1) rest
465+ return (Pair index var var' region : pairs, summaryVar)
287466
288467
289468-- EXPAND PATTERNS
@@ -324,22 +503,22 @@ data Info = Info
324503 { iSchemes :: [TypeScheme]
325504 , iRigid :: [Variable]
326505 , iFlex :: [Variable]
327− , iHeaders :: Map.Map String Type
506+ , iHeaders :: Map.Map String (A.Located Type)
328507 , iC2 :: TypeConstraint
329508 , iC1 :: TypeConstraint
330509 }
331510
332511
333512constrainDef :: Env.Environment -> Info -> Canonical.Def -> IO Info
334−constrainDef env info (Canonical.Definition (A.A _ pattern) expr maybeTipe) =
513+constrainDef env info (Canonical.Definition (A.A patternRegion pattern) expr maybeTipe) =
335514 let qs = [] -- should come from the def, but I'm not sure what would live there...
336515 in
337516 case (pattern, maybeTipe) of
338− (P.Var name, Just tipe) ->
339− constrainAnnotatedDef env info qs name expr tipe
517+ (P.Var name, Just (A.A typeRegion tipe)) ->
518+ constrainAnnotatedDef env info qs patternRegion typeRegion name expr tipe
340519
341520 (P.Var name, Nothing) ->
342− constrainUnannotatedDef env info qs name expr
521+ constrainUnannotatedDef env info qs patternRegion name expr
343522
344523 _ -> error ("problem in constrainDef with " ++ show pattern)
345524
@@ -348,11 +527,13 @@ constrainAnnotatedDef
348527 :: Env.Environment
349528 -> Info
350529 -> [String]
530+ -> R.Region
531+ -> R.Region
351532 -> String
352533 -> Canonical.Expr
353534 -> ST.Canonical
354535 -> IO Info
355−constrainAnnotatedDef env info qs name expr tipe =
536+constrainAnnotatedDef env info qs patternRegion typeRegion name expr tipe =
356537 do -- Some mistake may be happening here. Currently, qs is always [].
357538 rigidVars <- Monad.forM qs (\_ -> variable Rigid)
358539
@@ -369,14 +550,17 @@ constrainAnnotatedDef env info qs name expr tipe =
369550 { rigidQuantifiers = []
370551 , flexibleQuantifiers = flexiVars ++ vars
371552 , constraint = CTrue
372− , header = Map.singleton name typ
553+ , header = Map.singleton name (A.A patternRegion typ)
373554 }
374555
375− c <- constrain env' expr typ
556+ var <- variable Flexible
557+ defCon <- constrain env' expr (varN var)
558+ let annCon =
559+ CEqual (Error.BadTypeAnnotation name) typeRegion typ (varN var)
376560
377561 return $ info
378562 { iSchemes = scheme : iSchemes info
379− , iC1 = fl rigidVars c /\ iC1 info
563+ , iC1 = iC1 info /\ ex [var] (defCon /\ fl rigidVars annCon)
380564 }
381565
382566
@@ -384,10 +568,11 @@ constrainUnannotatedDef
384568 :: Env.Environment
385569 -> Info
386570 -> [String]
571+ -> R.Region
387572 -> String
388573 -> Canonical.Expr
389574 -> IO Info
390−constrainUnannotatedDef env info qs name expr =
575+constrainUnannotatedDef env info qs patternRegion name expr =
391576 do -- Some mistake may be happening here. Currently, qs is always [].
392577 rigidVars <- Monad.forM qs (\_ -> variable Rigid)
393578
@@ -399,11 +584,11 @@ constrainUnannotatedDef env info qs name expr =
399584
400585 let env' = env { Env.value = List.foldl' (\x f -> f x) (Env.value env) inserts }
401586
402− c <- constrain env' expr tipe
587+ con <- constrain env' expr tipe
403588
404589 return $ info
405590 { iRigid = rigidVars ++ iRigid info
406591 , iFlex = v : iFlex info
407− , iHeaders = Map.insert name tipe (iHeaders info)
408− , iC2 = c /\ iC2 info
592+ , iHeaders = Map.insert name (A.A patternRegion tipe) (iHeaders info)
593+ , iC2 = con /\ iC2 info
409594 }
src/Type/Constrain/Literal.hs+9−1
@@ -15,7 +15,7 @@ constrain
1515 -> IO T.TypeConstraint
1616constrain env region literal tipe =
1717 do tipe' <- litType
18− return (T.CEqual Error.None region tipe tipe')
18+ return (T.CEqual (Error.Literal name) region tipe tipe')
1919 where
2020 prim name =
2121 return (Env.get env Env.types name)
@@ -27,3 +27,11 @@ constrain env region literal tipe =
2727 L.Chr _ -> prim "Char"
2828 L.Str _ -> prim "String"
2929 L.Boolean _ -> prim "Bool"
30+
31+ name =
32+ case literal of
33+ L.IntNum _ -> "number"
34+ L.FloatNum _ -> "float"
35+ L.Chr _ -> "character"
36+ L.Str _ -> "string"
37+ L.Boolean _ -> "boolean"
src/Type/Constrain/Pattern.hs+34−13
@@ -22,7 +22,12 @@ constrain
2222 -> Type
2323 -> IO Fragment
2424constrain env (A.A region pattern) tipe =
25− let (===) = CEqual Error.None region
25+ let
26+ equal patternError leftType rightType =
27+ CEqual (Error.Pattern patternError) region leftType rightType
28+
29+ rvar v =
30+ A.A region (varN v)
2631 in
2732 case pattern of
2833 P.Anything ->
@@ -35,37 +40,53 @@ constrain env (A.A region pattern) tipe =
3540 P.Var name ->
3641 do v <- variable Flexible
3742 return $ Fragment
38− { typeEnv = Map.singleton name (varN v)
43+ { typeEnv = Map.singleton name (rvar v)
3944 , vars = [v]
40− , typeConstraint = varN v === tipe
45+ , typeConstraint =
46+ equal (Error.PVar name) (varN v) tipe
4147 }
4248
4349 P.Alias name p ->
4450 do v <- variable Flexible
4551 fragment <- constrain env p tipe
4652 return $ fragment
47− { typeEnv = Map.insert name (varN v) (typeEnv fragment)
53+ { typeEnv = Map.insert name (rvar v) (typeEnv fragment)
4854 , vars = v : vars fragment
49− , typeConstraint = varN v === tipe /\ typeConstraint fragment
55+ , typeConstraint =
56+ equal (Error.PAlias name) (varN v) tipe
57+ /\ typeConstraint fragment
5058 }
5159
5260 P.Data name patterns ->
53− do (_kind, cvars, args, result) <-
54− Env.freshDataScheme env (V.toString name)
61+ do let stringName = V.toString name
62+
63+ (_kind, cvars, args, result) <-
64+ Env.freshDataScheme env stringName
5565
5666 fragList <- Monad.zipWithM (constrain env) patterns args
5767 let fragment = joinFragments fragList
5868 return $ fragment
59− { typeConstraint = typeConstraint fragment /\ tipe === result
60− , vars = cvars ++ vars fragment
69+ { vars = cvars ++ vars fragment
70+ , typeConstraint =
71+ typeConstraint fragment
72+ /\ equal (Error.PData stringName) tipe result
6173 }
6274
6375 P.Record fields ->
64− do pairs <- mapM (\name -> (,) name <$> variable Flexible) fields
65− let tenv = Map.fromList (map (second varN) pairs)
66− c <- exists $ \t -> return (tipe === record (Map.map (:[]) tenv) t)
76+ do pairs <-
77+ mapM (\name -> (,) name <$> variable Flexible) fields
78+
79+ let tenv =
80+ Map.fromList (map (second rvar) pairs)
81+
82+ let unannotatedTenv =
83+ Map.map (\v -> [A.drop v]) tenv
84+
85+ con <- exists $ \t ->
86+ return (equal Error.PRecord tipe (record unannotatedTenv t))
87+
6788 return $ Fragment
6889 { typeEnv = tenv
6990 , vars = map snd pairs
70− , typeConstraint = c
91+ , typeConstraint = con
7192 }
src/Type/Environment.hs+3−3
@@ -117,7 +117,7 @@ get :: Environment -> (Environment -> Map.Map String a) -> String -> a
117117get env subDict key =
118118 Map.findWithDefault (error msg) key (subDict env)
119119 where
120− msg = "Could not find type constructor '" ++ key ++ "' while checking types."
120+ msg = "Could not find type constructor `" ++ key ++ "` while checking types."
121121
122122
123123freshDataScheme
@@ -210,8 +210,8 @@ instantiatorHelp env aliasVars sourceType =
210210 Just t -> return t
211211 Nothing ->
212212 error $
213− "Could not find type constructor '" ++
214− V.toString name ++ "' while checking types."
213+ "Could not find type constructor `" ++
214+ V.toString name ++ "` while checking types."
215215
216216 T.App t ts ->
217217 do t' <- go t
src/Type/ExtraChecks.hsdeleted−78
@@ -1,78 +0,0 @@
1−{-# OPTIONS_GHC -Wall #-}
2−
3−{-| This module contains checks to be run *after* type inference has completed
4−successfully. At that point we still need to ensure that `main` has an
5−acceptable type.
6−-}
7−module Type.ExtraChecks (mainType) where
8−
9−import Prelude hiding (maybe)
10−import Control.Monad.Except (ExceptT, throwError)
11−import qualified Data.Map as Map
12−
13−import qualified AST.Type as ST
14−import qualified AST.Variable as Var
15−
16−
17−-- MAIN TYPE
18−
19−mainType
20− :: (Monad m)
21− => Map.Map String ST.Canonical
22− -> ExceptT ST.Canonical m ()
23−mainType env =
24− case Map.lookup "main" env of
25− Nothing ->
26− return ()
27−
28− Just typeOfMain ->
29− let tipe = ST.deepDealias typeOfMain
30− in
31− if tipe `elem` validMainTypes
32− then return ()
33− else throwError typeOfMain
34−
35−
36−validMainTypes :: [ST.Canonical]
37−validMainTypes =
38− [ element
39− , html
40− , signal element
41− , signal html
42− ]
43− where
44− fromModule :: [String] -> String -> ST.Canonical
45− fromModule home name =
46− ST.Type (Var.fromModule home name)
47−
48− html =
49− fromModule ["VirtualDom"] "Node"
50−
51− signal tipe =
52− ST.App (fromModule ["Signal"] "Signal") [ tipe ]
53−
54− element =
55− let builtin name =
56− ST.Type (Var.builtin name)
57−
58− maybe tipe =
59− ST.App (fromModule ["Maybe"] "Maybe") [ tipe ]
60− in
61− ST.Record
62− [ ("element", fromModule ["Graphics","Element"] "ElementPrim")
63− , ("props",
64− ST.Record
65− [ ("click" , builtin "_Tuple0")
66− , ("color" , maybe (fromModule ["Color"] "Color"))
67− , ("height" , builtin "Int")
68− , ("hover" , builtin "_Tuple0")
69− , ("href" , builtin "String")
70− , ("id" , builtin "Int")
71− , ("opacity", builtin "Float")
72− , ("tag" , builtin "String")
73− , ("width" , builtin "Int")
74− ]
75− Nothing
76− )
77− ]
78− Nothing
src/Type/Fragment.hs+2−1
@@ -4,11 +4,12 @@ module Type.Fragment where
44import qualified Data.List as List
55import qualified Data.Map as Map
66
7+import Reporting.Annotation as A
78import Type.Type
89
910
1011data Fragment = Fragment
11− { typeEnv :: Map.Map String Type
12+ { typeEnv :: Map.Map String (A.Located Type)
1213 , vars :: [Variable]
1314 , typeConstraint :: TypeConstraint
1415 }
src/Type/Inference.hs+7−15
@@ -1,7 +1,7 @@
11module Type.Inference where
22
33import Control.Arrow (first, second)
4−import Control.Monad.Except (Except, forM, liftIO, runExceptT, throwError, withExceptT)
4+import Control.Monad.Except (Except, forM, liftIO, runExceptT, throwError)
55import qualified Data.Map as Map
66import qualified Data.Traversable as Traverse
77
@@ -12,7 +12,6 @@ import qualified Reporting.Annotation as A
1212import qualified Reporting.Error.Type as Error
1313import qualified Type.Constrain.Expression as TcExpr
1414import qualified Type.Environment as Env
15−import qualified Type.ExtraChecks as Check
1615import qualified Type.Solve as Solve
1716import qualified Type.State as TS
1817import qualified Type.Type as T
@@ -24,7 +23,7 @@ import System.IO.Unsafe
2423
2524infer
2625 :: Interfaces
27− -> CanonicalModule
26+ -> Module.CanonicalModule
2827 -> Except [A.Located Error.Error] (Map.Map String Type.Canonical)
2928infer interfaces modul =
3029 either throwError return $ unsafePerformIO $ runExceptT $
@@ -34,21 +33,14 @@ infer interfaces modul =
3433 state <- Solve.solve constraint
3534
3635 let header' = Map.delete "::" header
37− let types = Map.difference (TS.sSavedEnv state) header'
36+ let types = Map.map A.drop (Map.difference (TS.sSavedEnv state) header')
3837
39− srcTypes <-
40− liftIO (Traverse.traverse T.toSrcType types)
41−
42− withExceptT
43− (\tipe -> [A.A (error "TODO:infer") (Error.BadMain tipe)])
44− (Check.mainType srcTypes)
45−
46− return srcTypes
38+ liftIO (Traverse.traverse T.toSrcType types)
4739
4840
4941genConstraints
5042 :: Interfaces
51− -> CanonicalModule
43+ -> Module.CanonicalModule
5244 -> IO (Env.TypeDict, T.TypeConstraint)
5345genConstraints interfaces modul =
5446 do env <-
@@ -65,7 +57,7 @@ genConstraints interfaces modul =
6557 let allTypes = concat (ctors : importedVars)
6658 let vars = concatMap (fst . snd) allTypes
6759 let header = Map.map snd (Map.fromList allTypes)
68− let environ = T.CLet [ T.Scheme vars [] T.CTrue header ]
60+ let environ = T.CLet [ T.Scheme vars [] T.CTrue (Map.map (A.A undefined) header) ]
6961
7062 fvar <- T.variable T.Flexible
7163
@@ -85,7 +77,7 @@ canonicalizeValues env (moduleName, iface) =
8577 return (Module.nameToString moduleName ++ "." ++ name, tipe')
8678
8779
88−canonicalizeAdts :: Interfaces -> CanonicalModule -> [CanonicalAdt]
80+canonicalizeAdts :: Interfaces -> Module.CanonicalModule -> [CanonicalAdt]
8981canonicalizeAdts interfaces modul =
9082 localAdts ++ importedAdts
9183 where
src/Type/Solve.hs+55−39
@@ -1,14 +1,16 @@
11module Type.Solve (solve) where
22
3−import Control.Applicative ((<$>), (<*>))
3+import Control.Applicative ((<$>), (<*>), (<|>))
44import Control.Monad
55import Control.Monad.State (StateT, execStateT, liftIO)
66import Control.Monad.Except (ExceptT, throwError)
7+import qualified Data.Foldable as F
78import qualified Data.List as List
89import qualified Data.Map as Map
9−import qualified Data.Traversable as Traversable
10+import qualified Data.Traversable as T
1011import qualified Data.UnionFind.IO as UF
1112
13+import qualified AST.Type as Type
1214import qualified Reporting.Annotation as A
1315import qualified Reporting.Error.Type as Error
1416import qualified Type.State as TS
@@ -45,7 +47,7 @@ generalize youngPool =
4547 if isRedundant then return var else TS.register var
4648
4749 let rankDict' = Map.delete youngRank rankDict
48− Traversable.traverse (mapM registerIfNotRedundant) rankDict'
50+ T.traverse (mapM registerIfNotRedundant) rankDict'
4951
5052 -- For variables with rank youngRank
5153 -- If rank < youngRank: register in oldPool
@@ -159,23 +161,30 @@ actuallySolve constraint =
159161 do env <- TS.getEnv
160162 freshCopy <-
161163 case Map.lookup name env of
162− Just tipe ->
164+ Just (A.A _ tipe) ->
163165 TS.makeInstance tipe
166+
164167 Nothing ->
165168 if List.isPrefixOf "Native." name
166169 then liftIO (variable Flexible)
167− else error ("Could not find '" ++ name ++ "' when solving type constraints.")
170+ else error ("Could not find `" ++ name ++ "` when solving type constraints.")
168171
169172 t <- TS.flatten term
170− unify Error.None region freshCopy t
173+ unify (Error.Instance name) region freshCopy t
171174
172175
173−solveScheme :: TypeScheme -> StateT TS.SolverState IO (Map.Map String Variable)
176+solveScheme
177+ :: TypeScheme
178+ -> StateT TS.SolverState IO (Map.Map String (A.Located Variable))
174179solveScheme scheme =
180+ let
181+ flatten (A.A region term) =
182+ A.A region <$> TS.flatten term
183+ in
175184 case scheme of
176185 Scheme [] [] constraint header ->
177186 do actuallySolve constraint
178− Traversable.traverse TS.flatten header
187+ T.traverse flatten header
179188
180189 Scheme rigidQuantifiers flexibleQuantifiers constraint header ->
181190 do let quantifiers = rigidQuantifiers ++ flexibleQuantifiers
@@ -185,7 +194,7 @@ solveScheme scheme =
185194 freshPool <- TS.nextRankPool
186195 TS.switchToPool freshPool
187196 mapM TS.introduce quantifiers
188− header' <- Traversable.traverse TS.flatten header
197+ header' <- T.traverse flatten header
189198 actuallySolve constraint
190199
191200 allDistinct rigidQuantifiers
@@ -234,46 +243,53 @@ crash msg =
234243 ++ "<https://github.com/elm-lang/elm-compiler/issues>"
235244
236245
237−occurs :: (String, Variable) -> StateT TS.SolverState IO ()
238−occurs (_name, variable) =
239− do vars <- liftIO $ infiniteVars [] variable
240− case vars of
241− [] ->
242− return ()
243−
244− var : _ ->
245− do desc <- liftIO $ UF.descriptor var
246− let region = error "TODO:occurs"
247− case structure desc of
248− Nothing ->
249− TS.addError region (Error.InfiniteType undefined (error "TODO:occurs"))
250−
251− Just _ ->
252− TS.addError region (Error.InfiniteType undefined (error "TODO:occurs"))
253− where
254− infiniteVars :: [Variable] -> Variable -> IO [Variable]
255− infiniteVars seen var =
256− let go = infiniteVars (var:seen)
257− in
258− if var `elem` seen
259− then return [var]
260− else do
261− desc <- UF.descriptor var
246+occurs :: (String, A.Located Variable) -> StateT TS.SolverState IO ()
247+occurs (name, A.A region variable) =
248+ do maybePair <- liftIO $ occursHelp [] variable
249+ case maybePair of
250+ Nothing ->
251+ return ()
252+
253+ Just (t1, t2) ->
254+ let
255+ info = Error.InfiniteTypeInfo name t1 t2
256+ in
257+ TS.addError region (Error.InfiniteType info)
258+
259+
260+occursHelp
261+ :: [Variable]
262+ -> Variable
263+ -> IO (Maybe (Type.Canonical, Type.Canonical))
264+occursHelp seen var =
265+ if elem var seen then
266+ do desc <- UF.descriptor var
267+ UF.setDescriptor var (desc { structure = Nothing })
268+ var' <- UF.fresh desc
269+ srcVar <- toSrcType var
270+ srcVar' <- toSrcType var'
271+ return (Just (srcVar, srcVar'))
272+ else
273+ do desc <- UF.descriptor var
262274 case structure desc of
263− Nothing -> return []
275+ Nothing ->
276+ return Nothing
277+
264278 Just struct ->
279+ let go = occursHelp (var:seen)
280+ in
265281 case struct of
266282 App1 a b ->
267− (++) <$> go a <*> go b
283+ (<|>) <$> go a <*> go b
268284
269285 Fun1 a b ->
270− (++) <$> go a <*> go b
286+ (<|>) <$> go a <*> go b
271287
272288 Var1 a ->
273289 go a
274290
275291 EmptyRecord1 ->
276− return []
292+ return Nothing
277293
278294 Record1 fields ext ->
279− concat <$> mapM go (ext : concat (Map.elems fields))
295+ F.asum <$> mapM go (ext : concat (Map.elems fields))
src/Type/State.hs+1−1
@@ -33,7 +33,7 @@ emptyPool =
3333 }
3434
3535
36−type Env = Map.Map String Variable
36+type Env = Map.Map String (A.Located Variable)
3737
3838
3939-- Keeps track of the environment, type variable pool, and a list of errors
src/Type/Type.hs+21−13
@@ -13,6 +13,7 @@ import System.IO.Unsafe
1313
1414import qualified AST.Type as T
1515import qualified AST.Variable as Var
16+import qualified Reporting.Annotation as A
1617import qualified Reporting.Error.Type as Error
1718import qualified Reporting.Region as R
1819import Type.PrettyPrint
@@ -91,6 +92,7 @@ data Flex
9192 | Flexible
9293 | Constant
9394 | Is SuperType
95+ | Error
9496 deriving (Eq)
9597
9698data SuperType
@@ -118,7 +120,7 @@ data Scheme a b = Scheme
118120 { rigidQuantifiers :: [b]
119121 , flexibleQuantifiers :: [b]
120122 , constraint :: Constraint a b
121− , header :: Map.Map String a
123+ , header :: Map.Map String (A.Located a)
122124 }
123125
124126
@@ -151,20 +153,26 @@ namedVar flex name = UF.fresh $ Descriptor
151153
152154
153155variable :: Flex -> IO Variable
154−variable flex = UF.fresh $ Descriptor
155− { structure = Nothing
156− , rank = noRank
157− , flex = flex
158− , name = Nothing
159− , copy = Nothing
160− , mark = noMark
161− , alias = Nothing
162− }
156+variable flex =
157+ UF.fresh (descriptor flex)
158+
159+
160+descriptor :: Flex -> Descriptor
161+descriptor flex =
162+ Descriptor
163+ { structure = Nothing
164+ , rank = noRank
165+ , flex = flex
166+ , name = Nothing
167+ , copy = Nothing
168+ , mark = noMark
169+ , alias = Nothing
170+ }
163171
164172
165173-- CONSTRAINT HELPERS
166174
167−monoscheme :: Map.Map String a -> Scheme a b
175+monoscheme :: Map.Map String (A.Located a) -> Scheme a b
168176monoscheme headers =
169177 Scheme [] [] CTrue headers
170178
@@ -373,8 +381,8 @@ instance (PrettyType a, PrettyType b) => PrettyType (Scheme a b) where
373381 dict =
374382 P.parens . commaSep . map prettyPair $ Map.toList headers
375383
376− prettyPair (n,t) =
377− P.text n <+> P.text ":" <+> pretty Never t
384+ prettyPair (fieldName, A.A _ tipe) =
385+ P.text fieldName <+> P.text ":" <+> pretty Never tipe
378386
379387
380388-- CONVERT TO SOURCE TYPES
src/Type/Unify.hs+50−30
@@ -42,17 +42,20 @@ type Unify =
4242
4343
4444newtype RawMismatch =
45− RawMismatch (AstType.Canonical, AstType.Canonical, Error.Note)
45+ RawMismatch (AstType.Canonical, AstType.Canonical, Maybe String)
4646
4747
4848typeError
4949 :: UF.Point Descriptor
5050 -> UF.Point Descriptor
51− -> Error.Note
51+ -> Maybe String
5252 -> Unify a
5353typeError leftType rightType note =
5454 do leftSrcType <- liftIO (toSrcType leftType)
5555 rightSrcType <- liftIO (toSrcType rightType)
56+ liftIO $ do
57+ UF.setDescriptor leftType (Type.descriptor Error)
58+ UF.setDescriptor rightType (Type.descriptor Error)
5659 throwError (RawMismatch (leftSrcType, rightSrcType, note))
5760
5861
@@ -118,25 +121,31 @@ actuallyUnify variable1 variable2 = do
118121
119122 unifyNumber svar (Var.Canonical home name) =
120123 case home of
121− Var.BuiltIn | name `elem` ["Int","Float"] -> flexAndUnify svar
122− Var.Local | List.isPrefixOf "number" name -> flexAndUnify svar
124+ Var.BuiltIn | name `elem` ["Int","Float"] ->
125+ flexAndUnify svar
126+
127+ Var.Local | List.isPrefixOf "number" name ->
128+ flexAndUnify svar
123129 _ ->
124− typeError variable1 variable2 Error.NoNote
130+ typeError variable1 variable2 Nothing
125131
126132 comparableError maybeHint =
127− typeError variable1 variable2 $ Error.PostNote $
133+ typeError variable1 variable2 $ Just $
128134 "Note: comparable types include Int, Float, Char, String, lists, and tuples."
129135 ++ maybe "" ("\n"++) maybeHint
130136
131137 appendableError =
132− typeError variable1 variable2 $ Error.PostNote $
138+ typeError variable1 variable2 $ Just $
133139 "Note: appendable types include only Strings, Lists, and Text."
134140
135141 unifyComparable v (Var.Canonical home name) =
136142 case home of
137− Var.BuiltIn | name `elem` ["Int","Float","Char","String"] -> flexAndUnify v
138− Var.Local | List.isPrefixOf "comparable" name -> flexAndUnify v
139− _ -> comparableError Nothing
143+ Var.BuiltIn | name `elem` ["Int","Float","Char","String"] ->
144+ flexAndUnify v
145+ Var.Local | List.isPrefixOf "comparable" name ->
146+ flexAndUnify v
147+ _ ->
148+ comparableError Nothing
140149
141150 unifyComparableStructure varSuper varFlex =
142151 do struct <- liftIO $ collectApps varFlex
@@ -148,11 +157,11 @@ actuallyUnify variable1 variable2 = do
148157 do flexAndUnify varSuper
149158 unifyHelp v =<< liftIO (variable $ Is Comparable)
150159
151− Tuple vs
152− | length vs > 6 ->
160+ Tuple vs ->
161+ if length vs > 6 then
153162 comparableError $ Just $
154163 "Furthermore, it cannot be a tuple with more than 6 elements."
155− | otherwise ->
164+ else
156165 do flexAndUnify varSuper
157166 cmpVars <- liftIO $ forM [1..length vs] $ \_ -> variable (Is Comparable)
158167 zipWithM_ unifyHelp vs cmpVars
@@ -164,12 +173,8 @@ actuallyUnify variable1 variable2 = do
164173 _ -> appendableError
165174
166175 rigidError var =
167− let v = render (pretty Never var)
168− in
169− typeError variable1 variable2 $ Error.PreNote $
170− "Could not unify rigid type variable '" ++ v ++ "'. The problem probably\n"
171− ++ "relates to the type variable being shared between two type annotations.\n"
172− ++ "Try commenting out some type annotations and see what happens."
176+ typeError variable1 variable2 $ Just $
177+ rigidErrorMessage (render (pretty Never var))
173178
174179 superUnify =
175180 case (flex desc1, flex desc2, name desc1, name desc2) of
@@ -195,17 +200,20 @@ actuallyUnify variable1 variable2 = do
195200
196201 (Rigid, _, _, _) -> rigidError variable1
197202 (_, Rigid, _, _) -> rigidError variable2
198− _ -> typeError variable1 variable2 Error.NoNote
203+ _ -> typeError variable1 variable2 Nothing
199204
200205 case (structure desc1, structure desc2) of
201− (Nothing, Nothing) | flex desc1 == Flexible && flex desc1 == Flexible ->
202− merge
206+ (Nothing, Nothing)
207+ | flex desc1 == Flexible && flex desc1 == Flexible ->
208+ merge
203209
204− (Nothing, _) | flex desc1 == Flexible ->
205− merge2
210+ (Nothing, _)
211+ | flex desc1 == Flexible -> merge2
212+ | flex desc1 == Error -> return ()
206213
207− (_, Nothing) | flex desc2 == Flexible ->
208− merge1
214+ (_, Nothing)
215+ | flex desc2 == Flexible -> merge1
216+ | flex desc2 == Error -> return ()
209217
210218 (Just (Var1 v), _) ->
211219 unifyHelp v variable2
@@ -239,7 +247,19 @@ actuallyUnify variable1 variable2 = do
239247 (Record1 _ _, Record1 _ _) ->
240248 recordUnify fresh variable1 variable2
241249
242− _ -> typeError variable1 variable2 Error.NoNote
250+ _ -> typeError variable1 variable2 Nothing
251+
252+
253+-- RIGID ERROR
254+
255+rigidErrorMessage :: String -> String
256+rigidErrorMessage var =
257+ "Could not unify user provided type variable `" ++ var ++ "`. The most likely cases are:\n\n"
258+ ++ " 1. The type you wrote down is too general. It says any type can go through\n"
259+ ++ " but in fact only a certain type can.\n"
260+ ++ " 2. A type variable is probably being shared between two type annotations.\n"
261+ ++ " They are treated as different things in the current implementation, so\n"
262+ ++ " Try commenting out some type annotations and see what happens."
243263
244264
245265-- RECORD UNIFICATION
@@ -262,14 +282,14 @@ recordUnify fresh variable1 variable2 =
262282 let uniqueFields2 = diffFields fields2 fields1
263283
264284 let addFieldMismatchError missingFields =
265− typeError variable1 variable2 $ Error.PreNote $
285+ typeError variable1 variable2 $ Just $
266286 fieldMismatchError missingFields
267287
268288 case (ext1, ext2) of
269289 (Empty _, Empty _) ->
270290 case Map.null uniqueFields1 && Map.null uniqueFields2 of
271291 True -> return ()
272− False -> typeError variable1 variable2 Error.NoNote
292+ False -> typeError variable1 variable2 Nothing
273293
274294 (Empty var1, Extension var2) ->
275295 case (Map.null uniqueFields1, Map.null uniqueFields2) of
@@ -356,7 +376,7 @@ fieldMismatchError missingFields =
356376 case Map.keys missingFields of
357377 [] -> ""
358378 [key] ->
359− "Looks like a record is missing the field '" ++ key ++ "'"
379+ "Looks like a record is missing the field `" ++ key ++ "`"
360380 keys ->
361381 "Looks like a record is missing fields "
362382 ++ List.intercalate ", " (init keys) ++ ", and " ++ last keys
src/Validate.hs+60−33
@@ -23,34 +23,43 @@ import qualified Reporting.Result as Result
2323-- VALIDATE DECLARATIONS
2424
2525declarations
26− :: [D.SourceDecl]
26+ :: Bool
27+ -> [D.SourceDecl]
2728 -> Result.Result wrn Error.Error [D.ValidDecl]
28−declarations sourceDecls =
29− do validDecls <- validateDecls sourceDecls
29+declarations isRoot sourceDecls =
30+ do validDecls <- validateDecls Nothing sourceDecls
3031 (\_ _ -> validDecls)
3132 <$> declDuplicates validDecls
32− <*> T.traverse checkDecl validDecls
33+ <*> T.traverse (checkDecl isRoot) validDecls
3334
3435
3536validateDecls
36− :: [D.SourceDecl]
37+ :: Maybe String
38+ -> [D.SourceDecl]
3739 -> Result.Result wrn Error.Error [D.ValidDecl]
38−validateDecls sourceDecls =
40+validateDecls maybeComment sourceDecls =
3941 case sourceDecls of
4042 [] ->
4143 return []
4244
43− decl : decls ->
44− validateDeclsHelp decl decls
45+ sourceDecl : decls ->
46+ case sourceDecl of
47+ D.Comment comment ->
48+ validateDecls (Just comment) decls
49+
50+ D.Decl decl ->
51+ validateDeclsHelp maybeComment decl decls
4552
4653
4754validateDeclsHelp
48− :: D.SourceDecl
55+ :: Maybe String
56+ -> A.Located D.SourceDecl'
4957 -> [D.SourceDecl]
5058 -> Result.Result wrn Error.Error [D.ValidDecl]
51−validateDeclsHelp (A.A region decl) decls =
59+validateDeclsHelp comment (A.A region decl) decls =
5260 let addRest validDecl =
53− (:) (A.A region validDecl) <$> validateDecls decls
61+ (:) (A.A (region, comment) validDecl)
62+ <$> validateDecls Nothing decls
5463 in
5564 case decl of
5665 D.Datatype name tvars ctors ->
@@ -63,21 +72,23 @@ validateDeclsHelp (A.A region decl) decls =
6372 addRest (D.Fixity assoc prec op)
6473
6574 D.Definition def ->
66− defHelp def decls
75+ defHelp comment def decls
6776
6877 D.Port port ->
69− portHelp region port decls
78+ portHelp comment region port decls
7079
7180
7281-- VALIDATE DEFINITIONS IN DECLARATIONS
7382
7483defHelp
75− :: Source.Def
84+ :: Maybe String
85+ -> Source.Def
7686 -> [D.SourceDecl]
7787 -> Result.Result wrn Error.Error [D.ValidDecl]
78−defHelp (A.A region def) decls =
88+defHelp comment (A.A region def) decls =
7989 let addRest def' rest =
80− (:) (A.A region (D.Definition def')) <$> validateDecls rest
90+ (:) (A.A (region, comment) (D.Definition def'))
91+ <$> validateDecls Nothing rest
8192 in
8293 case def of
8394 Source.Definition pat expr ->
@@ -87,7 +98,8 @@ defHelp (A.A region def) decls =
8798
8899 Source.TypeAnnotation name tipe ->
89100 case decls of
90− A.A _ (D.Definition (A.A _ (Source.Definition pat@(A.A _ (Pattern.Var name')) expr))) : rest
101+ D.Decl (A.A _ (D.Definition (A.A _
102+ (Source.Definition pat@(A.A _ (Pattern.Var name')) expr)))) : rest
91103 | name == name' ->
92104 do expr' <- Validate.expression expr
93105 let def' = Valid.Definition pat expr' (Just tipe)
@@ -100,13 +112,15 @@ defHelp (A.A region def) decls =
100112-- VALIDATE PORTS IN DECLARATIONS
101113
102114portHelp
103− :: R.Region
115+ :: Maybe String
116+ -> R.Region
104117 -> D.SourcePort
105118 -> [D.SourceDecl]
106119 -> Result.Result wrn Error.Error [D.ValidDecl]
107−portHelp region port decls =
120+portHelp comment region port decls =
108121 let addRest port' rest =
109− (:) (A.A region (D.Port port')) <$> validateDecls rest
122+ (:) (A.A (region,comment) (D.Port port'))
123+ <$> validateDecls Nothing rest
110124 in
111125 case port of
112126 D.PortDefinition name _ ->
@@ -114,7 +128,7 @@ portHelp region port decls =
114128
115129 D.PortAnnotation name tipe ->
116130 case decls of
117− A.A _ (D.Port (D.PortDefinition name' expr)) : rest
131+ D.Decl (A.A _ (D.Port (D.PortDefinition name' expr))) : rest
118132 | name == name' ->
119133 do expr' <- Validate.expression expr
120134 let port' = D.Out name expr' tipe
@@ -199,8 +213,10 @@ expression (A.A ann sourceExpression) =
199213 <$> expression funcExpr
200214 <*> expression argExpr
201215
202− MultiIf branches ->
203− MultiIf <$> T.traverse both branches
216+ MultiIf branches finally ->
217+ MultiIf
218+ <$> T.traverse both branches
219+ <*> expression finally
204220
205221 Access record field ->
206222 Access
@@ -249,6 +265,9 @@ expression (A.A ann sourceExpression) =
249265 do expr' <- expression expr
250266 return (Task name expr' tipe)
251267
268+ Crash details ->
269+ return (Crash details)
270+
252271
253272second :: (a, Source.Expr) -> Result.Result wrn Error.Error (a, Valid.Expr)
254273second (value, expr) =
@@ -304,7 +323,7 @@ declDuplicates decls =
304323
305324
306325extractValues :: D.ValidDecl -> ([A.Located String], [A.Located String])
307−extractValues (A.A region decl) =
326+extractValues (A.A (region, _) decl) =
308327 case decl of
309328 D.Definition (Valid.Definition pattern _ _) ->
310329 ( Pattern.boundVars pattern
@@ -339,8 +358,8 @@ extractValues (A.A region decl) =
339358
340359-- UNBOUND TYPE VARIABLES
341360
342−checkDecl :: D.ValidDecl -> Result.Result wrn Error.Error ()
343−checkDecl (A.A region decl) =
361+checkDecl :: Bool -> D.ValidDecl -> Result.Result wrn Error.Error ()
362+checkDecl isRoot (A.A (region,_) decl) =
344363 case decl of
345364 D.Definition _ ->
346365 return ()
@@ -352,21 +371,29 @@ checkDecl (A.A region decl) =
352371 [] ->
353372 return ()
354373
355− v:vs ->
356− Result.throw region
357− (Error.UnboundTypeVarsInUnion name boundVars v vs ctors)
374+ unbound ->
375+ let
376+ (v:vs) = Set.toList (Set.fromList unbound)
377+ in
378+ Result.throw region
379+ (Error.UnboundTypeVarsInUnion name boundVars v vs ctors)
358380
359381 D.TypeAlias name boundVars tipe ->
360382 case findUnbound boundVars (freeVars tipe) of
361383 [] ->
362384 return ()
363385
364− v:vs ->
365− Result.throw region
366− (Error.UnboundTypeVarsInAlias name boundVars v vs tipe)
386+ unbound ->
387+ let
388+ (v:vs) = Set.toList (Set.fromList unbound)
389+ in
390+ Result.throw region
391+ (Error.UnboundTypeVarsInAlias name boundVars v vs tipe)
367392
368393 D.Port _ ->
369− return ()
394+ if isRoot
395+ then return ()
396+ else Result.throw region Error.UnexpectedPort
370397
371398 D.Fixity _ _ _ ->
372399 return ()
tests/Test.hs+3−3
@@ -3,12 +3,12 @@ module Main where
33import Test.Framework
44
55import Test.Compiler
6−import Test.Property
6+--import Test.Property
77
88
9−main :: IO ()
9+main :: IO ()
1010main =
1111 defaultMain
1212 [ compilerTests
13− , propertyTests
13+-- , propertyTests
1414 ]
tests/Test/Compiler.hs+25−10
@@ -12,7 +12,6 @@ import Test.Framework.Providers.HUnit (testCase)
1212import Test.HUnit (Assertion, assertFailure, assertBool)
1313
1414import qualified Elm.Compiler as Compiler
15−import qualified Elm.Compiler.Module as Module
1615
1716
1817compilerTests :: Test
@@ -45,16 +44,25 @@ testsDir =
4544-- RUN COMPILER
4645
4746testIf
48− :: (Either String (Module.Interface, String) -> Assertion)
47+ :: (Either String Compiler.Result -> Assertion)
4948 -> [FilePath]
5049 -> IO [Test]
5150testIf handleResult filePaths =
5251 traverse setupTest filePaths
5352 where
54− setupTest filePath =
55− do source <- readFile filePath
56− let result = Compiler.compile "elm-lang" "core" source Map.empty
57− return (testCase filePath (handleResult result))
53+ setupTest filePath = do
54+ source <- readFile filePath
55+
56+ let context =
57+ Compiler.Context "elm-lang" "core" True False
58+ let (dealiaser, _warnings, result) =
59+ Compiler.compile context source Map.empty
60+ let formatErrors errors =
61+ concatMap (Compiler.errorToString dealiaser filePath source) errors
62+ let formattedResult =
63+ either (Left . formatErrors) Right result
64+
65+ return $ testCase filePath (handleResult formattedResult)
5866
5967
6068-- CHECK RESULTS
@@ -62,12 +70,19 @@ testIf handleResult filePaths =
6270isSuccess :: Either String a -> Assertion
6371isSuccess result =
6472 case result of
65− Right _ -> assertBool "" True
66− Left msg -> assertFailure msg
73+ Right _ ->
74+ assertBool "" True
75+
76+ Left errorMessages ->
77+ assertFailure errorMessages
6778
6879
6980isFailure :: Either a b -> Assertion
7081isFailure result =
7182 case result of
72− Right _ -> assertFailure "Compilation succeeded but should have failed"
73− Left _ -> assertBool "" True
83+ Right _ ->
84+ assertFailure "Compilation succeeded but should have failed"
85+
86+ Left _ ->
87+ assertBool "" True
88+
tests/Test/Property.hs+1−1
@@ -12,7 +12,7 @@ import Text.PrettyPrint as P
1212import AST.Literal as Lit
1313import qualified AST.Pattern as P
1414import qualified AST.Variable as V
15−import AST.PrettyPrint (Pretty, pretty)
15+import Reporting.PrettyPrint (Pretty, pretty)
1616import Parse.Helpers (IParser, iParse)
1717import Parse.Literal (literal)
1818import qualified Parse.Pattern as Pat (expr)
tests/test-files/good/Ports.elm+11−11
@@ -1,21 +1,21 @@
11
22-- incoming
3−input userID : String
4−input number : Int
5−input tuple : (Float,Bool)
6−input array : List Int
7−input record : { x:Float, y:Float }
3+port userID : String
4+port number : Int
5+port tuple : (Float,Bool)
6+port array : List Int
7+port record : { x:Float, y:Float }
88
99-- outgoing
10−output fortyTwo : Int
11−output fortyTwo =
10+port fortyTwo : Int
11+port fortyTwo =
1212 42
1313
1414
15−output time : Float
16−output time =
15+port time : Float
16+port time =
1717 3.14
1818
19−output students : List { name:String, age:Int }
20−output students =
19+port students : List { name:String, age:Int }
20+port students =
2121 [ { name = "Tom", age = 42 } ]
tests/test-files/good/PortsWithTypeAliases.elm+2−2
@@ -7,7 +7,7 @@ type alias Point x y =
77 { x:x, y:y }
88
99
10−input points : Point2D
10+port points : Point2D
1111
1212
13−input pairs : Point String String
13+port pairs : Point String String