on master help sign in
Get parse errors working for many primitive cases

Still does not work when there are multiple paths that all do not work.

commit768a565df9a21f9d460ce56678752c8305057faa authorEvan Czaplicki <evancz@users.noreply.github.com> dateSat 10 Dec 2016, 17:02 (10 years ago) parent4adc799
2 files changed+277−60
src/Parse/Primitives.hs+40−35 src/Reporting/Error/Syntax.hs+237−25
src/Parse/Primitives.hs+40−35
@@ -416,37 +416,39 @@ infixOp =
416416 eerr (loneTheory row col InfixOp)
417417
418418 else
419− case infixOpHelp array offset length row col of
420− Left err ->
421− cerr err
422−
423− Right ( newOffset, newLength, newCol ) ->
424− let
425− !size =
426− newOffset - offset
427−
428− makeArray =
429− do mutableArray <- Text.new size
430− Text.copyI mutableArray 0 array offset size
431− return mutableArray
432− in
433− if size == 0 then
434− eerr (loneTheory row col InfixOp)
435−
436− else
437− case Text.Text (Text.run makeArray) 0 size of
438− "=" -> cerr (ParseError row col Dot)
439− "|" -> cerr (ParseError row col Pipe)
440− ":" -> cerr (ParseError row col Arrow)
441− "." -> cerr (ParseError row col Equals)
442− "->" -> cerr (ParseError row col HasType)
443− op -> cok op (State array newOffset newLength indent row newCol)
419+ let
420+ (Text.Iter char size) = peek array offset
421+ in
422+ if not (Help.isSymbol char) then
423+ eerr (loneTheory row col InfixOp)
424+ else
425+ case infixOpHelp array (offset + size) (length - size) row (col + 1) of
426+ Left err ->
427+ cerr err
428+
429+ Right ( newOffset, newLength, newCol ) ->
430+ let
431+ !newSize =
432+ newOffset - offset
433+
434+ makeArray =
435+ do mutableArray <- Text.new newSize
436+ Text.copyI mutableArray 0 array offset newSize
437+ return mutableArray
438+ in
439+ case Text.Text (Text.run makeArray) 0 newSize of
440+ "=" -> cerr (ParseError row col Dot)
441+ "|" -> cerr (ParseError row col Pipe)
442+ ":" -> cerr (ParseError row col Arrow)
443+ "." -> cerr (ParseError row col Equals)
444+ "->" -> cerr (ParseError row col HasType)
445+ op -> cok op (State array newOffset newLength indent row newCol)
444446
445447
446448infixOpHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError (Int, Int, Int)
447449infixOpHelp array offset length row col =
448450 if length == 0 then
449− Left (ParseError row col (EndOfFile "infix operator"))
451+ Right ( offset, length, col )
450452
451453 else
452454 let
@@ -599,7 +601,7 @@ eatMultiComment array offset length row col =
599601eatMultiCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
600602eatMultiCommentHelp array offset length row col openComments =
601603 if length == 0 then
602− Left (ParseError row col (EndOfFile "multi-line comment"))
604+ Left (ParseError row col EndOfFile_Comment)
603605
604606 else
605607 let
@@ -703,7 +705,7 @@ isQuote array offset =
703705singleString :: Text.Array -> Int -> Int -> Int -> Int -> Int -> LB.Builder -> Either ParseError ( Int, Int, Int, Text.Text )
704706singleString array offset length row col initialOffset builder =
705707 if length == 0 then
706− Left (ParseError row col (EndOfFile "string"))
708+ Left (ParseError row col EndOfFile_String)
707709
708710 else
709711 let
@@ -724,7 +726,7 @@ singleString array offset length row col initialOffset builder =
724726 Left (ParseError row col NewLineInString)
725727
726728 else if word == 0x005C {- \ -} then
727− case eatEscape array (offset + 1) (length - 1) row (col + 1) of
729+ case eatEscape array (offset + 1) (length - 1) row (col + 1) EndOfFile_String of
728730 Left err ->
729731 Left err
730732
@@ -743,10 +745,10 @@ singleString array offset length row col initialOffset builder =
743745 singleString array (offset + 2) (length - 2) row (col + 1) initialOffset builder
744746
745747
746−eatEscape :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Char )
747−eatEscape array offset length row col =
748+eatEscape :: Text.Array -> Int -> Int -> Int -> Int -> Problem -> Either ParseError ( Int, Char )
749+eatEscape array offset length row col problem =
748750 if length == 0 then
749− Left (ParseError row col (EndOfFile "escape sequence"))
751+ Left (ParseError row col problem)
750752
751753 else
752754 case Text.unsafeIndex array offset of
@@ -804,7 +806,7 @@ eatHex array offset length n =
804806multiString :: Text.Array -> Int -> Int -> Int -> Int -> Int -> LB.Builder -> Either ParseError ( Int, Int, Int, Text.Text )
805807multiString array offset length row col initialOffset builder =
806808 if length == 0 then
807− Left (ParseError row col (EndOfFile "multi-line string"))
809+ Left (ParseError row col EndOfFile_MultiString)
808810
809811 else
810812 let
@@ -822,7 +824,7 @@ multiString array offset length row col initialOffset builder =
822824 Right ( offset + 3, length - 3, col + 3, str )
823825
824826 else if word == 0x005C {- \ -} then
825− case eatEscape array (offset + 1) (length - 1) row (col + 1) of
827+ case eatEscape array (offset + 1) (length - 1) row (col + 1) EndOfFile_MultiString of
826828 Left err ->
827829 Left err
828830
@@ -887,7 +889,7 @@ characterHelp array offset length row col =
887889 Left (ParseError row col BadChar)
888890
889891 else if word == 0x005C {- \ -} then
890− eatEscape array (offset + 1) (length - 1) row (col + 1)
892+ eatEscape array (offset + 1) (length - 1) row (col + 1) BadChar
891893
892894 else if word == 0x000A {- \n -} then
893895 Left (ParseError row col BadChar)
@@ -990,6 +992,9 @@ chompInt array startOffset offset length =
990992 else if word == 0x002E {- . -} then
991993 chompFraction array startOffset (offset + 1) (length - 1)
992994
995+ else if word == 0x0065 {- e -} || word == 0x0045 {- E -} then
996+ chompExponent array startOffset (offset + 1) (length - 1)
997+
993998 else
994999 Right ( offset, length, readInt array startOffset offset )
9951000
src/Reporting/Error/Syntax.hs+237−25
@@ -8,13 +8,14 @@ module Reporting.Error.Syntax
88 )
99 where
1010
11+import qualified Data.Set as Set
1112import Data.Text (Text)
1213
1314import qualified Reporting.Render.Type as RenderType
1415import qualified Reporting.Region as Region
1516import qualified Reporting.Report as Report
1617import qualified Reporting.Helpers as Help
17−import Reporting.Helpers ((<>), dullyellow, hsep, i2t, text)
18+import Reporting.Helpers ((<>), dullyellow, hsep, i2t, reflowParagraph, text)
1819
1920
2021
@@ -59,7 +60,9 @@ data ParseError
5960
6061data Problem
6162 = Tab
62− | EndOfFile Text
63+ | EndOfFile_Comment
64+ | EndOfFile_String
65+ | EndOfFile_MultiString
6366 | NewLineInString
6467 | BadEscape
6568 | BadChar
@@ -69,11 +72,11 @@ data Problem
6972 | BadNumberHex
7073 | BadNumberZero
7174 | BadChunkInQualifiedCapVar
72− | HasType -- "A single colon is for type annotations. Maybe you want :: instead? Or maybe you are defining a type annotation, but there is whitespace before it?"
73− | Equals -- "The = operator is reserved for defining variables. Maybe you want == instead? Or maybe you are defining a variable, but there is whitespace before it?"
74− | Arrow -- "Arrows are reserved for cases and anonymous functions. Maybe you want > or >= instead?"
75− | Pipe -- "Vertical bars are reserved for use in union type declarations. Maybe you want || instead?"
76− | Dot -- "Dots are for record access. They cannot float around on their own!"
75+ | HasType
76+ | Equals
77+ | Arrow
78+ | Pipe
79+ | Dot
7780 | Theories [Theory]
7881
7982
@@ -86,6 +89,7 @@ data Theory
8689 | BadIndent
8790 | FreshLine
8891 | Expecting Text
92+ deriving (Eq, Ord)
8993
9094
9195
@@ -95,15 +99,15 @@ data Theory
9599toReport :: RenderType.Localizer -> Error -> Report.Report
96100toReport _localizer err =
97101 case err of
98− Parse _ ->
99− Report.report "PARSE ERROR" Nothing "Something is wrong." (text "TODO")
102+ Parse problem ->
103+ problemToReport problem
100104
101105 BadFunctionName arity ->
102106 Report.report
103107 "BAD FUNCTION DEFINITION"
104108 Nothing
105109 "Complex patterns cannot be used as function names."
106− ( Help.reflowParagraph $
110+ ( reflowParagraph $
107111 "This function definition has " <> i2t arity <> " arguments, but instead\
108112 \ of a normal name like `add` or `reverse` it has a pattern. There is no\
109113 \ way to \"deconstruct\" a function with pattern matching, so this needs to\
@@ -117,7 +121,7 @@ toReport _localizer err =
117121 ("The free variable `" <> name <> "` appears more than once in this pattern.")
118122 ( Help.stack
119123 [ text "Rename the variables so there are no duplicates."
120− , Help.reflowParagraph $
124+ , reflowParagraph $
121125 "In Elm, pattern matching works by binding these free variables to subsections\
122126 \ of the matching value. It does not make sense to have the same name for two\
123127 \ different subsections though! When you say `" <> name <> "` in some code, there\
@@ -130,7 +134,7 @@ toReport _localizer err =
130134 "STRAY COMMENT"
131135 Nothing
132136 ("This documentation comment is not followed by anything.")
133− ( Help.reflowParagraph $
137+ ( reflowParagraph $
134138 "All documentation comments need to be right above the declaration they\
135139 \ describe. Maybe some code got deleted or commented out by accident? Or\
136140 \ maybe this comment is here by accident?"
@@ -141,7 +145,7 @@ toReport _localizer err =
141145 "BAD MODULE DECLARATION"
142146 Nothing
143147 "A normal module can expose values, but not settings like this."
144− ( Help.reflowParagraph $
148+ ( reflowParagraph $
145149 "If you want a normal module, just remove this stuff. If you want to create\
146150 \ an `effect module` you just forgot to use the `effect` keyword. In that\
147151 \ case, just change `module` to `effect module` and you should be headed in\
@@ -153,7 +157,7 @@ toReport _localizer err =
153157 "BAD MODULE DECLARATION"
154158 Nothing
155159 "A port module can expose values, but not have settings like this."
156− ( Help.reflowParagraph $
160+ ( reflowParagraph $
157161 "If you want a port module, just remove this stuff. If you want to create\
158162 \ a custom effect module instead (which is rare) just change `port module`\
159163 \ to `effect module` and you should be headed in the right direction!"
@@ -164,7 +168,7 @@ toReport _localizer err =
164168 "EFFECT MODULE PROBLEM"
165169 Nothing
166170 ("You have defined " <> Help.functionName name <> " multiple times.")
167− ( Help.reflowParagraph $
171+ ( reflowParagraph $
168172 "There can only be one " <> Help.functionName name
169173 <> " though! Remove until there is one left."
170174 )
@@ -174,7 +178,7 @@ toReport _localizer err =
174178 "EFFECT MODULE PROBLEM"
175179 Nothing
176180 ("Setting " <> Help.functionName name <> " is not recognized.")
177− ( Help.reflowParagraph $
181+ ( reflowParagraph $
178182 "Remove this entry and you should be all set!"
179183 )
180184
@@ -183,7 +187,7 @@ toReport _localizer err =
183187 "EFFECT MODULE PROBLEM"
184188 Nothing
185189 ("You are defining an effect module, but it has no settings.")
186− ( Help.reflowParagraph $
190+ ( reflowParagraph $
187191 "If you just wanted a normal module, change the keywords `effect module`\
188192 \ to `module` and you should be all set. If you want a proper effect module,\
189193 \ you need to specify your commands and/or subscriptions. Read more about this\
@@ -195,7 +199,7 @@ toReport _localizer err =
195199 "EFFECT MODULE PROBLEM"
196200 Nothing
197201 ("You are defining an effect module, but there is no " <> Help.functionName name <> " defined.")
198− ( Help.reflowParagraph $
202+ ( reflowParagraph $
199203 "There is a small set of top-level functions and values that must be defined\
200204 \ in any complete effect module. The best thing is probably to just read more\
201205 \ about effect modules here:\
@@ -207,7 +211,7 @@ toReport _localizer err =
207211 "BAD PORT"
208212 Nothing
209213 ("You are declaring port " <> Help.functionName name <> " in a normal module.")
210− ( Help.reflowParagraph $
214+ ( reflowParagraph $
211215 "All ports must be defined in a `port module`. You should probably have just\
212216 \ one of these for your project. This way all of your foreign interactions\
213217 \ stay relatively organized."
@@ -233,7 +237,7 @@ toReport _localizer err =
233237 <> "` is used more than once in the arguments of "
234238 <> Help.functionName funcName <> "."
235239 )
236− ( Help.reflowParagraph $
240+ ( reflowParagraph $
237241 "Rename things until `" <> argName <> "` is used only once.\
238242 \ Otherwise how can we tell which one you want when you\
239243 \ say `" <> argName <> "` it in the body of your function?"
@@ -253,7 +257,7 @@ toReport _localizer err =
253257 ( "Naming multiple top-level values " <> Help.functionName name <> " makes things\n"
254258 <> "ambiguous. When you say " <> Help.functionName name <> " which one do you want?"
255259 )
256− ( Help.reflowParagraph $
260+ ( reflowParagraph $
257261 "Find all the top-level values named " <> Help.functionName name
258262 <> " and do some renaming. Make sure the names are distinct!"
259263 )
@@ -264,7 +268,7 @@ toReport _localizer err =
264268 Nothing
265269 ( "There are multiple types named `" <> name <> "` in this module."
266270 )
267− ( Help.reflowParagraph $
271+ ( reflowParagraph $
268272 "Search through this module, find all the types named `"
269273 <> name <> "`, and give each of them a unique name."
270274 )
@@ -275,7 +279,7 @@ toReport _localizer err =
275279 Nothing
276280 ("There are multiple values named `" <> name <> "` in this let-expression."
277281 )
278− ( Help.reflowParagraph $
282+ ( reflowParagraph $
279283 "Search through this let-expression, find all the values named `"
280284 <> name <> "`, and give each of them a unique name."
281285 )
@@ -307,7 +311,7 @@ toReport _localizer err =
307311 ( "Type alias `" <> typeName <> "` has some problems with type variables."
308312 )
309313 ( Help.stack
310− [ Help.reflowParagraph $
314+ [ reflowParagraph $
311315 "The declaration says it uses certain type variables ("
312316 <> Help.commaSep unused <> ") but they do not appear in the aliased type. "
313317 <> "Furthermore, the aliased type says it uses type variables ("
@@ -334,9 +338,217 @@ unboundTypeVars declKind typeName givenVars unboundVars =
334338 map text (declKind : typeName : givenVars)
335339 ++ map (dullyellow . text) unboundVars
336340 ++ map text ["=", "..."]
337− , Help.reflowParagraph $
341+ , reflowParagraph $
338342 "Here's why. Imagine one `" <> typeName <> "` where `" <> head unboundVars <>
339343 "` is an Int and another where it is a Bool. When we explicitly list the type\
340344 \ variables, the type checker can see that they are actually different types."
341345 ]
342346 )
347+
348+
349+
350+-- PARSE ERROR TO REPORT
351+
352+
353+parseReport :: Text -> Help.Doc -> Report.Report
354+parseReport pre post =
355+ Report.report "PARSE ERROR" Nothing pre post
356+
357+
358+problemToReport :: Problem -> Report.Report
359+problemToReport problem =
360+ case problem of
361+ Tab ->
362+ parseReport
363+ "I ran into a tab, but tabs are not allowed in Elm files."
364+ (reflowParagraph "Replace the tab with spages.")
365+
366+ EndOfFile_Comment ->
367+ parseReport
368+ "I got to the end of the file while parsing a multi-line comment."
369+ ( Help.stack
370+ [ reflowParagraph $
371+ "Multi-line comments look like {- comment -}, and it looks like\
372+ \ you are missing the closing marker."
373+ , reflowParagraph $
374+ "Nested multi-line comments like {- this {- and this -} -} are allowed.\
375+ \ That means the opening and closing markers must be balanced, just\
376+ \ like parentheses in normal code. Maybe that is the problem?"
377+ ]
378+ )
379+
380+ EndOfFile_String ->
381+ parseReport
382+ "I got to the end of the file while parsing a string."
383+ (reflowParagraph $
384+ "Strings look like \"this\" with double quotes on each end.\
385+ \ So the closing double quote seems to be missing."
386+ )
387+
388+ EndOfFile_MultiString ->
389+ parseReport
390+ "I got to the end of the file while parsing a multi-line string."
391+ (reflowParagraph $
392+ "Multi-line strings begin and end with three double quotes in a\
393+ \ row, like \"\"\"this\"\"\". So the closing marker seems\
394+ \ to be missing."
395+ )
396+
397+ NewLineInString ->
398+ parseReport
399+ "This string is missing the closing quote."
400+ ( Help.stack
401+ [ reflowParagraph $
402+ "Elm strings must start and end with a double quote, like \"this\"."
403+ , reflowParagraph $
404+ "If you want a string that can contain newlines, use three double quotes in a\
405+ \ row, like \"\"\"this\"\"\"."
406+ ]
407+ )
408+
409+ BadEscape ->
410+ parseReport
411+ "Ran into a bad escape code."
412+ ( reflowParagraph
413+ "Elm allows typical escape characters like \\n and \\t, but you can\
414+ \ also use \\x0040 to refer to unicode characters by their hex code.\
415+ \ It seems like something has gone wrong with one of these codes."
416+ )
417+
418+ BadChar ->
419+ parseReport
420+ "Ran into a bad use of single quotes."
421+ ( Help.stack
422+ [ reflowParagraph $
423+ "If you want to create a string, just switch to double quotes:"
424+ , Help.indent 4 $
425+ dullyellow (text "'this'")
426+ <> text " => "
427+ <> dullyellow (text "\"this\"")
428+ , reflowParagraph $
429+ "Unlike JavaScript, Elm distinguishes between strings like \"hello\"\
430+ \ and individual characters like 'A' and '3'. It is not all strings!\
431+ \ If you really do want a character though, something went wrong\
432+ \ and I did not find the closing single quote."
433+ ]
434+ )
435+
436+ BadNumberDot ->
437+ parseReport
438+ "Numbers cannot end with a decimal points."
439+ ( reflowParagraph $
440+ "Leave it off or add some digits after it, like or 3 or 3.0"
441+ )
442+
443+ BadNumberEnd ->
444+ parseReport
445+ "Numbers cannot have letters or underscores in them."
446+ ( reflowParagraph $
447+ "Maybe a space is missing between a number and a variable?"
448+ )
449+
450+ BadNumberExp ->
451+ parseReport
452+ "If you put the letter E in a number, it should followed by more digits."
453+ ( reflowParagraph $
454+ "If you want to say 1000, you can also say 1e3.\
455+ \ You cannot just end it with an E though!"
456+ )
457+
458+ BadNumberHex ->
459+ parseReport
460+ "I see the start of a hex number, but not the end."
461+ ( reflowParagraph $
462+ "A hex number looks like 0x123ABC, where the 0x is followed by hexidecimal digits (i.e. 0123456789abcdefABCDEF)"
463+ )
464+
465+ BadNumberZero ->
466+ parseReport
467+ "Normal numbers cannot start with a zero. Take the zeros off the front."
468+ ( reflowParagraph $
469+ "Only numbers like 0x0040 or 0.25 can start with a zero."
470+ )
471+
472+ BadChunkInQualifiedCapVar ->
473+ error "TODO"
474+
475+ HasType ->
476+ parseReport
477+ "Single colons should only show up in types."
478+ ( reflowParagraph $
479+ "Maybe you want :: instead? Or maybe you are defining\
480+ \ a type annotation but there are extra spaces before it?"
481+ )
482+
483+ Equals ->
484+ parseReport
485+ "The = operator is reserved for defining variables."
486+ ( reflowParagraph $
487+ "Maybe you want == instead? Or maybe you are defining a\
488+ \ variable but there are extra spaces before it?"
489+ )
490+
491+ Arrow ->
492+ parseReport
493+ "Arrows are reserved for cases and anonymous functions."
494+ ( reflowParagraph $
495+ "Maybe you want > or >= instead?"
496+ )
497+
498+ Pipe ->
499+ parseReport
500+ "Vertical bars are reserved for use in type declarations."
501+ ( reflowParagraph $
502+ "Maybe you want || instead?"
503+ )
504+
505+ Dot ->
506+ parseReport
507+ "Dots are for record access and decimal points."
508+ ( reflowParagraph $
509+ "They cannot float around on their own though, so\
510+ \ maybe there is some extra whitespace?"
511+ )
512+
513+ Theories allTheories ->
514+ case Set.toList (Set.fromList allTheories) of
515+ [] ->
516+ parseReport
517+ "Something went wrong when parsing this code."
518+ ( reflowParagraph $
519+ "I do not have any suggestions though, and I suspect there is some\
520+ \ sort of bug. Can you get it down to an <http://sscce.org> and\
521+ \ share it at <https://github.com/elm-lang/error-message-catalog/issues>?\
522+ \ That way we can figure out how to give better advice!"
523+ )
524+
525+ theories ->
526+ error "TODO" (map theoryToText theories)
527+
528+
529+theoryToText :: Theory -> Text
530+theoryToText theory =
531+ case theory of
532+ Keyword _ ->
533+ ""
534+
535+ Symbol _ ->
536+ ""
537+
538+ Variable ->
539+ ""
540+
541+ InfixOp ->
542+ ""
543+
544+ Digit ->
545+ "a digit, like 0 or 6"
546+
547+ BadIndent ->
548+ ""
549+
550+ FreshLine ->
551+ ""
552+
553+ Expecting _ ->
554+ ""