@@ -49,7 +49,7 @@ import Prelude hiding (Char, String)
4949import qualified Data.Char as Char
5050import qualified Data.Name as Name
5151import Data.Word (Word16)
52−--import Numeric (showHex)
52+import Numeric (showHex)
5353
5454import qualified Elm.ModuleName as ModuleName
5555import Parse.Primitives (Row, Col)
@@ -470,7 +470,7 @@ data TTuple
470470data Char
471471 = CharEndless
472472 | CharEscape Escape
473− | CharNotString Int
473+ | CharNotString Word16
474474
475475
476476data String
@@ -480,10 +480,10 @@ data String
480480
481481
482482data Escape
483− = EscapeUnknown Word16 Word16
484− | BadUnicodeFormat Word16 Word16
485− | BadUnicodeCode Word16 Word16
486− | BadUnicodeLength Word16 Word16 Int Int
483+ = EscapeUnknown
484+ | BadUnicodeFormat Word16
485+ | BadUnicodeCode Word16
486+ | BadUnicodeLength Word16 Int Int
487487
488488
489489data Number
@@ -550,7 +550,7 @@ toReport source err =
550550 ++ ModuleName.toChars expectedName
551551 ++ "` here. Make the following change, and you should be all set!"
552552 , D.indent 4 $
553− D.red (D.fromName actualName) <> " -> " <> D.green (D.fromName expectedName)
553+ D.dullyellow (D.fromName actualName) <> " -> " <> D.green (D.fromName expectedName)
554554 , D.toSimpleNote $
555555 "I require that module names correspond to file paths. This makes it much\
556556 \ easier to explore unfamiliar codebases! So if you want to keep the current\
@@ -835,11 +835,11 @@ toExprReport source _ expr _ _ =
835835 Start _ _ ->
836836 error "TODO Start"
837837
838− Char _ _ _ ->
839− error "TODO Char"
838+ Char char row col ->
839+ toCharReport source char row col
840840
841− String _ _ _ ->
842− error "TODO String"
841+ String string row col ->
842+ toStringReport source string row col
843843
844844 Number number row col ->
845845 toNumberReport source number row col
@@ -884,6 +884,196 @@ toExprReport source _ expr _ _ =
884884 error "TODO IndentMoreExpr"
885885
886886
887+toCharReport :: Code.Source -> Char -> Row -> Col -> Report.Report
888+toCharReport source char row col =
889+ case char of
890+ CharEndless ->
891+ let
892+ region = toRegion row col
893+ in
894+ Report.Report "MISSING SINGLE QUOTE" region [] $
895+ Report.toCodeSnippet source region Nothing
896+ (
897+ D.reflow $
898+ "I thought I was parsing a character, but I got to the end of\
899+ \ the line without seeing the closing single quote:"
900+ ,
901+ D.reflow $
902+ "Add a closing single quote here!"
903+ )
904+
905+ CharEscape escape ->
906+ toEscapeReport source escape row col
907+
908+ CharNotString width ->
909+ let
910+ region = toWiderRegion row col width
911+ in
912+ Report.Report "NEEDS DOUBLE QUOTES" region [] $
913+ Report.toCodeSnippet source region Nothing
914+ (
915+ "The following string uses single quotes:"
916+ ,
917+ D.stack
918+ [ "Please switch to double quotes instead:"
919+ , D.indent 4 $
920+ D.dullyellow "'this'" <> " => " <> D.green "\"this\""
921+ , D.toSimpleNote $
922+ "Elm uses double quotes for strings like \"hello\", whereas it uses single\
923+ \ quotes for individual characters like 'a' and 'ø'. This distinction helps with\
924+ \ code like (String.any (\\c -> c == 'X') \"90210\") where you are inspecting\
925+ \ individual characters."
926+ ]
927+ )
928+
929+
930+toStringReport :: Code.Source -> String -> Row -> Col -> Report.Report
931+toStringReport source string row col =
932+ case string of
933+ StringEndless_Single ->
934+ let
935+ region = toRegion row col
936+ in
937+ Report.Report "ENDLESS STRING" region [] $
938+ Report.toCodeSnippet source region Nothing
939+ (
940+ D.reflow $
941+ "I got to the end of the line without seeing the closing double quote:"
942+ ,
943+ D.stack
944+ [ D.fillSep $
945+ ["Strings","look","like",D.green "\"this\"","with","double"
946+ ,"quotes","on","each","end.","Is","the","closing","double"
947+ ,"quote","missing","in","your","code?"
948+ ]
949+ , D.link "Note" "Read" "TODO" "if you want a string that spans multiple lines."
950+ ]
951+ )
952+
953+ StringEndless_Multi ->
954+ let
955+ region = toWiderRegion row col 3
956+ in
957+ Report.Report "ENDLESS STRING" region [] $
958+ Report.toCodeSnippet source region Nothing
959+ (
960+ D.reflow $
961+ "I cannot find the end of this multi-line string:"
962+ ,
963+ D.stack
964+ [ D.reflow "Add a \"\"\" somewhere after this to end the string."
965+ , D.link "Hint" "Read" "TODO" "for more information on strings and multi-line strings."
966+ ]
967+ )
968+
969+ StringEscape escape ->
970+ toEscapeReport source escape row col
971+
972+
973+toEscapeReport :: Code.Source -> Escape -> Row -> Col -> Report.Report
974+toEscapeReport source escape row col =
975+ case escape of
976+ EscapeUnknown ->
977+ let
978+ region = toWiderRegion row col 2
979+ in
980+ Report.Report "UNKNOWN ESCAPE" region [] $
981+ Report.toCodeSnippet source region Nothing
982+ (
983+ D.reflow $
984+ "Backslashes always start escaped characters, but I do not recognize this one:"
985+ ,
986+ D.stack
987+ [ D.reflow $
988+ "Valid escape characters include:"
989+ , D.dullyellow $ D.indent 4 $ D.vcat $
990+ [ "\\n"
991+ , "\\r"
992+ , "\\t"
993+ , "\\\""
994+ , "\\\'"
995+ , "\\\\"
996+ , "\\u{003D}"
997+ ]
998+ , D.reflow $
999+ "Do you want one of those instead? Maybe you need \\\\ to escape a backslash?"
1000+ , D.toSimpleNote $
1001+ "The last style lets encode ANY character by its Unicode code\
1002+ \ point. That means \\u{0009} and \\t are the same. You can use\
1003+ \ that style for anything not covered by the other six escapes!"
1004+ ]
1005+ )
1006+
1007+ BadUnicodeFormat width ->
1008+ let
1009+ region = toWiderRegion row col width
1010+ in
1011+ Report.Report "BAD UNICODE ESCAPE" region [] $
1012+ Report.toCodeSnippet source region Nothing
1013+ (
1014+ D.reflow $
1015+ "I ran into an invalid Unicode escape:"
1016+ ,
1017+ D.stack
1018+ [ D.reflow $
1019+ "Here are some examples of valid Unicode escapes:"
1020+ , D.dullyellow $ D.indent 4 $ D.vcat $
1021+ [ "\\u{0041}"
1022+ , "\\u{03BB}"
1023+ , "\\u{6728}"
1024+ , "\\u{1F60A}"
1025+ ]
1026+ , D.reflow $
1027+ "Notice that the code point is always surrounded by curly braces.\
1028+ \ Maybe you are missing the opening or closing curly brace?"
1029+ ]
1030+ )
1031+ BadUnicodeCode width ->
1032+ let
1033+ region = toWiderRegion row col width
1034+ in
1035+ Report.Report "BAD UNICODE ESCAPE" region [] $
1036+ Report.toCodeSnippet source region Nothing
1037+ (
1038+ D.reflow $
1039+ "This is not a valid code point:"
1040+ ,
1041+ D.reflow $
1042+ "The valid code points are between 0 and 10FFFF inclusive."
1043+ )
1044+
1045+ BadUnicodeLength width numDigits badCode ->
1046+ let
1047+ region = toWiderRegion row col width
1048+ in
1049+ Report.Report "BAD UNICODE ESCAPE" region [] $
1050+ Report.toCodeSnippet source region Nothing $
1051+ if numDigits < 4 then
1052+ (
1053+ D.reflow $
1054+ "Every code point needs at least four digits:"
1055+ ,
1056+ let
1057+ goodCode = replicate (4 - numDigits) '0' ++ map Char.toUpper (showHex badCode "")
1058+ suggestion = "\\u{" <> D.fromChars goodCode <> "}"
1059+ in
1060+ D.fillSep ["Try",D.green suggestion,"instead?"]
1061+ )
1062+
1063+ else
1064+ (
1065+ D.reflow $
1066+ "This code point has too many digits:"
1067+ ,
1068+ D.fillSep $
1069+ ["Valid","code","points","are","between"
1070+ ,D.green "\\u{0000}","and",D.green "\\u{10FFFF}" <> ","
1071+ ,"so","try","trimming","any","leading","zeros","until"
1072+ ,"you","have","between","four","and","six","digits."
1073+ ]
1074+ )
1075+
1076+
8871077toNumberReport :: Code.Source -> Number -> Row -> Col -> Report.Report
8881078toNumberReport source number row col =
8891079 let
@@ -1051,233 +1241,6 @@ toNumberReport source number row col =
10511241 "I need a single digit between 0 and 9 inclusive."
10521242 )
10531243
1054− -- Parse.Utf8
1055−
1056− CharStart ->
1057− Report.Report "EXPECTING A CHARACTER" region [] $
1058− Report.toCodeSnippet source region Nothing
1059− (
1060− D.reflow $
1061− "I was expecting a character here:"
1062− ,
1063− D.stack
1064− [ D.reflow $
1065− "Characters start and end with single quotes, like this:"
1066− , D.indent 4 $ D.vcat $
1067− [ "'a'"
1068− , "'b'"
1069− , "'ø'"
1070− , "'\\n'"
1071− , "'\\u{00F8}'"
1072− ]
1073− , D.reflow $
1074− "That last style lets you encode characters by their unicode\
1075− \ code point, so 'a' is equivalent to '\\u{0061}', 'ø' is\
1076− \ equivalent to '\\u{00F8}', and so on for all code points."
1077− ]
1078− )
1079−
1080− CharEnd ->
1081− Report.Report "MISSING SINGLE QUOTE" region [] $
1082− Report.toCodeSnippet source region Nothing
1083− (
1084− D.reflow $
1085− "I thought I was parsing a character, but I got to the end of\
1086− \ the line without seeing the closing single quote:"
1087− ,
1088− D.stack
1089− [ D.reflow $
1090− "Characters start and end with single quotes, like this:"
1091− , D.indent 4 $ D.vcat $
1092− [ "'a'"
1093− , "'b'"
1094− , "'ø'"
1095− , "'\\n'"
1096− , "'\\u{00F8}'"
1097− ]
1098− , D.reflow $
1099− "That last style lets you encode characters by their unicode\
1100− \ code point, so 'a' is equivalent to '\\u{0061}', 'ø' is\
1101− \ equivalent to '\\u{00F8}', and so on for all code points."
1102− ]
1103− )
1104−
1105− CharNotString startRow startCol ->
1106− let
1107− stringRegion =
1108− A.Region (A.Position startRow startCol) pos
1109− in
1110− Report.Report "NEEDS DOUBLE QUOTES" stringRegion [] $
1111− Report.toCodeSnippet source stringRegion Nothing
1112− (
1113− "The following string uses single quotes:"
1114− ,
1115− D.stack
1116− [ "Please switch to double quotes instead:"
1117− , D.indent 4 $
1118− D.dullyellow "'this'" <> " => " <> D.green "\"this\""
1119− , D.toSimpleNote $
1120− "Elm uses double quotes for strings like \"hello\", whereas it uses single\
1121− \ quotes for individual characters like 'a' and 'ø'. This distinction helps with\
1122− \ code like (String.any (\\c -> c == 'X') \"90210\") where you are inspecting\
1123− \ individual characters."
1124− ]
1125− )
1126−
1127− StringStart ->
1128− Report.Report "EXPECTING A STRING" region [] $
1129− Report.toCodeSnippet source region Nothing
1130− (
1131− D.reflow $
1132− "I was expecting a string here:"
1133− ,
1134− D.fillSep
1135− ["Something","like"
1136− ,D.green "\"this\""
1137− ,"or"
1138− ,D.green "\"that\""
1139− ,"with","double","quotes."
1140− ]
1141− )
1142−
1143− StringEnd_Single ->
1144− Report.Report "ENDLESS STRING" region [] $
1145− Report.toCodeSnippet source region Nothing
1146− (
1147− D.reflow $
1148− "I got to the end of the line without seeing the closing double quote:"
1149− ,
1150− D.stack
1151− [ D.fillSep $
1152− ["Strings","look","like",D.green "\"this\"","with","double"
1153− ,"quotes","on","each","end.","Is","the","closing","double"
1154− ,"quote","missing","in","your","code?"
1155− ]
1156− , D.link "Note" "Read" "TODO" "if you want a string that spans multiple lines."
1157− ]
1158− )
1159−
1160− StringEnd_Multi startRow startCol ->
1161− let
1162− startRegion =
1163− A.Region
1164− (A.Position startRow startCol)
1165− (A.Position startRow (startCol + 3))
1166− in
1167− Report.Report "ENDLESS STRING" startRegion [] $
1168− Report.toCodeSnippet source startRegion Nothing
1169− (
1170− D.reflow $
1171− "I cannot find the end of this multi-line string:"
1172− ,
1173− D.stack
1174− [ D.reflow "Add a \"\"\" somewhere after this to end the string."
1175− , D.link "Hint" "Read" "TODO" "for more information on strings and multi-line strings."
1176− ]
1177− )
1178−
1179− EscapeUnknown startCol endCol ->
1180− let
1181− escapeRegion =
1182− A.Region (A.Position row startCol) (A.Position row endCol)
1183− in
1184− Report.Report "UNKNOWN ESCAPE" escapeRegion [] $
1185− Report.toCodeSnippet source escapeRegion Nothing
1186− (
1187− D.reflow $
1188− "Backslashes always start escaped characters, but I do not recognize this one:"
1189− ,
1190− D.stack
1191− [ D.reflow $
1192− "Valid escape characters include:"
1193− , D.indent 4 $ D.vcat $
1194− [ "\\n"
1195− , "\\r"
1196− , "\\t"
1197− , "\\\""
1198− , "\\\'"
1199− , "\\\\"
1200− , "\\u{03BB}"
1201− ]
1202− , D.reflow $
1203− "The last one lets encode ANY character by its Unicode code\
1204− \ point, so use that for anything outside the ordinary six."
1205− ]
1206− )
1207−
1208− BadUnicodeFormat startCol endCol ->
1209− let
1210− escapeRegion =
1211− A.Region (A.Position row startCol) (A.Position row endCol)
1212− in
1213− Report.Report "BAD UNICODE ESCAPE" escapeRegion [] $
1214− Report.toCodeSnippet source escapeRegion Nothing
1215− (
1216− D.reflow $
1217− "I ran into an invalid Unicode escape:"
1218− ,
1219− D.stack
1220− [ D.reflow $
1221− "Here are some examples of valid Unicode escapes:"
1222− , D.green $ D.indent 4 $ D.vcat $
1223− [ "\\u{0041}"
1224− , "\\u{03BB}"
1225− , "\\u{6728}"
1226− , "\\u{1F60A}"
1227− ]
1228− , D.reflow $
1229− "Notice that the code point is always surrounded by curly\
1230− \ braces. They are required!"
1231− ]
1232− )
1233−
1234− BadUnicodeCode startCol endCol ->
1235− let
1236− escapeRegion =
1237− A.Region (A.Position row startCol) (A.Position row endCol)
1238− in
1239− Report.Report "BAD UNICODE ESCAPE" escapeRegion [] $
1240− Report.toCodeSnippet source escapeRegion Nothing
1241− (
1242− D.reflow $
1243− "This is not a valid code point:"
1244− ,
1245− D.reflow $
1246− "The valid code points are between 0 and 10FFFF inclusive."
1247− )
1248−
1249− BadUnicodeLength startCol endCol numDigits badCode ->
1250− let
1251− escapeRegion =
1252− A.Region (A.Position row startCol) (A.Position row endCol)
1253− in
1254− Report.Report "BAD UNICODE ESCAPE" escapeRegion [] $
1255− Report.toCodeSnippet source escapeRegion Nothing $
1256− if numDigits < 4 then
1257− (
1258− D.reflow $
1259− "Every code point needs at least four digits:"
1260− ,
1261− let
1262− goodCode = replicate (4 - numDigits) '0' ++ showHex badCode ""
1263− escape = "\\u{" <> D.fromChars goodCode <> "}"
1264− in
1265− D.fillSep ["Try",D.green escape,"instead?"]
1266− )
1267−
1268− else
1269− (
1270− D.reflow $
1271− "This code point has too many digits:"
1272− ,
1273− D.fillSep $
1274− ["Valid","code","points","are","between"
1275− ,D.green "\\u{0000}","and",D.green "\\u{10FFFF}" <> ","
1276− ,"so","try","trimming","any","leading","zeros","until"
1277− ,"you","have","between","four","and","six","digits."
1278− ]
1279− )
1280−
12811244 Operator ->
12821245 Report.Report "EXPECTING AN OPERATOR" region [] $
12831246 Report.toCodeSnippet source region Nothing