on master help sign in
Restore parsers for string and char literals
6a13d3eEvan Czaplicki
src/Parse/Helpers.hs+1−1
@@ -197,7 +197,7 @@ spacePrefix p =
197constrainedSpacePrefix :: IParser a -> (String -> IParser ()) -> IParser [a]
197constrainedSpacePrefix :: IParser a -> (String -> IParser ()) -> IParser [a]
198constrainedSpacePrefix parser constraint =
198constrainedSpacePrefix parser constraint =
199 many $ choice
199 many $ choice
200− [ try (spacing >> lookAhead (oneOf "[({")) >> parser
200+ [ try (spacing >> lookAhead (oneOf "[({\"'")) >> parser
201 , try (spacing >> parser)
201 , try (spacing >> parser)
202 ]
202 ]
203 where
203 where
src/Parse/Literal.hs+150−52
@@ -3,17 +3,20 @@ module Parse.Literal (literal, shader) where
3
3
4import Prelude hiding (exponent)
4import Prelude hiding (exponent)
5import Control.Monad (join)
5import Control.Monad (join)
6+import qualified Data.Char as Char
7+import qualified Data.List as List
6import qualified Data.Map as Map
8import qualified Data.Map as Map
7import qualified Language.GLSL.Parser as GLP
9import qualified Language.GLSL.Parser as GLP
8import qualified Language.GLSL.Syntax as GLS
10import qualified Language.GLSL.Syntax as GLS
9import Text.Parsec
11import Text.Parsec
10− ( ParseError, (<|>), (<?>), anyChar, char, choice, digit, hexDigit
12+ ( ParseError, (<|>), anyChar, char, choice, digit, getPosition
11− , lookAhead, many1, manyTill, notFollowedBy, option, parserFail
13+ , hexDigit, lookAhead, many1, option, parserFail, satisfy
12− , string, try
14+ , setPosition, string, try, upper
13 )
15 )
14
16
15−import Parse.Helpers (IParser, expecting)
16import qualified AST.Literal as L
17import qualified AST.Literal as L
18+import Parse.Helpers (IParser, expecting, failure)
19+import qualified Reporting.Error.Syntax as Error
17
20
18
21
19
22
@@ -24,8 +27,8 @@ literal :: IParser L.Literal
24literal =
27literal =
25 choice
28 choice
26 [ toLiteral <$> rawNumber
29 [ toLiteral <$> rawNumber
27− , L.Str <$> str
30+ , stringLiteral
28− , L.Chr <$> chr
31+ , charLiteral
29 ]
32 ]
30
33
31
34
@@ -89,74 +92,169 @@ exponent =
89
92
90
93
91
94
92−-- STRINGS
95+-- CHARACTERS
93
96
94
97
95−str :: IParser String
98+charLiteral :: IParser L.Literal
96−str =
99+charLiteral =
97− expecting "a string like \"hello\"" $
100+ expecting "a character like 'c'" $
101+ do start <- getPosition
102+ char '\''
103+ firstChar <-
98− choice [ multiStr, singleStr ]
104+ choice
105+ [ char '\\' >> escapeCode
106+ , satisfy (\c -> c /= '\'' && c > '\026')
107+ ]
108+ choice
109+ [ do char '\''
110+ return (L.Chr firstChar)
111+ , do setPosition start
112+ failure Error.singleQuotes
113+ ]
99
114
100
115
101−delimitedSequence :: IParser a -> IParser String -> IParser String
102−delimitedSequence quote insides =
103− do quote
104− concat <$> manyTill insides quote
105
116
117+-- STRINGS
106
118
107−multiStr :: IParser String
108−multiStr =
109− delimitedSequence (try (string "\"\"\"")) multilineStringChar
110
119
120+stringLiteral :: IParser L.Literal
121+stringLiteral =
122+ expecting "a string like \"hello\"" $
123+ do char '"'
124+ choice
125+ [ do try (string "\"\"")
126+ multiStringClose mempty
127+ , do singleStringClose mempty
128+ ]
111
129
112−singleStr :: IParser String
113−singleStr =
114− delimitedSequence (char '"') ((:[]) <$> anyChar)
115
130
131+singleStringClose :: (String -> String) -> IParser L.Literal
132+singleStringClose builder =
133+ choice
134+ [ do char '"'
135+ return (L.Str (builder ""))
136+ , do char '\\'
137+ choice
138+ [ do char '&'
139+ singleStringClose builder
140+ , do c <- escapeCode
141+ singleStringClose (\end -> builder (c : end))
142+ ]
143+ , do c <- satisfy (\c -> c > '\026')
144+ singleStringClose (\end -> builder (c : end))
145+ , failure "Not expecting a character like this in a string."
146+ ]
116
147
117
148
118−-- CHARACTERS
149+multiStringClose :: (String -> String) -> IParser L.Literal
150+multiStringClose builder =
151+ choice
152+ [ do try (string "\"\"\"")
153+ return (L.Str (builder ""))
154+ , do char '\n'
155+ multiStringClose (\end -> builder ('\n' : end))
156+ , do char '\\'
157+ choice
158+ [ do char '&'
159+ multiStringClose builder
160+ , do c <- escapeCode
161+ multiStringClose (\end -> builder (c : end))
162+ ]
163+ , do c <- satisfy (\c -> c > '\026')
164+ multiStringClose (\end -> builder (c : end))
165+ , failure "Not expecting a character like this in a multi-line string."
166+ ]
119
167
120
168
121−chr :: IParser Char
169+escapeCode :: IParser Char
122−chr =
170+escapeCode =
123− expecting "a character like 'x'" $
171+ choice
124− do char '\''
172+ [ do char '^'
125− myChar <- anyChar <?> "a character"
173+ code <- upper
126− char '\''
174+ return (toEnum (fromEnum code - fromEnum 'A' + 1))
127− return myChar
175+ -- NUMBERS
176+ , do parseCode 10 digit
177+ , do char 'x'
178+ parseCode 16 hexDigit
179+ -- ESCAPES
180+ , c2c 'a' '\a'
181+ , c2c 'b' '\b'
182+ , c2c 'f' '\f'
183+ , c2c 'n' '\n'
184+ , c2c 'r' '\r'
185+ , c2c 't' '\t'
186+ , c2c 'v' '\v'
187+ , c2c '"' '"'
188+ , c2c '\\' '\\'
189+ , c2c '\'' '\''
190+ -- ASCII CODES
191+ , s2c "BS" '\BS'
192+ , s2c "HT" '\HT'
193+ , s2c "LF" '\LF'
194+ , s2c "VT" '\VT'
195+ , s2c "FF" '\FF'
196+ , s2c "CR" '\CR'
197+ , s2c "SO" '\SO'
198+ , s2c "SI" '\SI'
199+ , s2c "EM" '\EM'
200+ , s2c "FS" '\FS'
201+ , s2c "GS" '\GS'
202+ , s2c "RS" '\RS'
203+ , s2c "US" '\US'
204+ , s2c "SP" '\SP'
205+ , s2c "NUL" '\NUL'
206+ , s2c "SOH" '\SOH'
207+ , s2c "STX" '\STX'
208+ , s2c "ETX" '\ETX'
209+ , s2c "EOT" '\EOT'
210+ , s2c "ENQ" '\ENQ'
211+ , s2c "ACK" '\ACK'
212+ , s2c "BEL" '\BEL'
213+ , s2c "DLE" '\DLE'
214+ , s2c "DC1" '\DC1'
215+ , s2c "DC2" '\DC2'
216+ , s2c "DC3" '\DC3'
217+ , s2c "DC4" '\DC4'
218+ , s2c "NAK" '\NAK'
219+ , s2c "SYN" '\SYN'
220+ , s2c "ETB" '\ETB'
221+ , s2c "CAN" '\CAN'
222+ , s2c "SUB" '\SUB'
223+ , s2c "ESC" '\ESC'
224+ , s2c "DEL" '\DEL'
225+ ]
128
226
129−{-
130− failure $
131− "Elm uses double quotes for strings. Switch\
132− \ the ' to \" on both ends of the string and\
133− \ you should be all set!"
134−-}
135
227
228+c2c :: Char -> Char -> IParser Char
229+c2c escapedChar resultingChar =
230+ do char escapedChar
231+ return resultingChar
136
232
137−-- CHARACTER HELPERS
138
233
234+s2c :: String -> Char -> IParser Char
235+s2c escapedString resultingChar =
236+ do string escapedString
237+ return resultingChar
139
238
140−multilineStringChar :: IParser String
141−multilineStringChar =
142− do notFollowedBy (string "\"\"\"")
143− choice
144− [ newlineChar
145− , escapeQuotes <$> anyChar
146− ]
147
239
240+parseCode :: Integer -> IParser Char -> IParser Char
241+parseCode base baseDigit =
242+ do start <- getPosition
243+ code <- parseNumber base baseDigit
244+ if code <= 0x10FFFF
245+ then
246+ return (toEnum (fromInteger code))
148
247
149−escapeQuotes :: Char -> String
150−escapeQuotes c =
151− if c == '\"' then "\\\"" else [c]
248+ else
249+ do setPosition start
250+ fail "This code is too big! UTF-8 only goes to 10FFFF."
152
251
153
252
154−newlineChar :: IParser String
253+parseNumber :: Integer -> IParser Char -> IParser Integer
155−newlineChar =
254+parseNumber base baseDigit =
156− choice
255+ do digits <- many1 baseDigit
157− [ char '\n' >> return "\\n"
256+ let n = List.foldl' (\x d -> base * x + toInteger (Char.digitToInt d)) 0 digits
158− , char '\r' >> return "\\r"
257+ seq n (return n)
159− ]
160
258
161
259
162
260
src/Reporting/Error/Syntax.hs+13
@@ -311,6 +311,10 @@ tab :: String
311tab = "TAB"
311tab = "TAB"
312
312
313
313
314+singleQuotes :: String
315+singleQuotes = "SINGLE_QUOTES"
316+
317+
314keyword :: String -> String
318keyword :: String -> String
315keyword kwd =
319keyword kwd =
316 "KEYWORD=" ++ kwd
320 "KEYWORD=" ++ kwd
@@ -324,6 +328,7 @@ prime var =
324data SpecialMessage
328data SpecialMessage
325 = MsgKeyword String
329 = MsgKeyword String
326 | MsgPrime String
330 | MsgPrime String
331+ | MsgSingleQuotes
327 | MsgTab
332 | MsgTab
328
333
329
334
@@ -338,6 +343,9 @@ extractSpecialMessage message =
338 else if tab == message then
343 else if tab == message then
339 Just MsgTab
344 Just MsgTab
340
345
346+ else if singleQuotes == message then
347+ Just MsgSingleQuotes
348+
341 else
349 else
342 Nothing
350 Nothing
343
351
@@ -397,6 +405,11 @@ parseErrorReport messages =
397 , Just "I am looking for spaces, not tabs."
405 , Just "I am looking for spaces, not tabs."
398 )
406 )
399
407
408+ Just MsgSingleQuotes ->
409+ ( "It likes you are trying to create a string with single quotes."
410+ , Just "Switch from ' to \" and you should be all set!"
411+ )
412+
400 Nothing ->
413 Nothing ->
401 ( msg
414 ( msg
402 , Nothing
415 , Nothing