@@ -1,3 +1,4 @@
1module Parse.Helpers where
2module Parse.Helpers where
3import Prelude hiding (until)
4import Prelude hiding (until)
@@ -10,14 +11,16 @@ import Data.Map (Map)
10import qualified Data.Map as Map
11import qualified Data.Map as Map
11import qualified Language.GLSL.Parser as GLP
12import qualified Language.GLSL.Parser as GLP
12import Language.GLSL.Syntax
13import Language.GLSL.Syntax
14+import Text.Parsec hiding (newline,spaces,State)
15+import Text.Parsec.Indent
16+import qualified Text.Parsec.Token as T
13import SourceSyntax.Helpers as Help
18import SourceSyntax.Helpers as Help
14import SourceSyntax.Literal as Literal
19import SourceSyntax.Literal as Literal
15import SourceSyntax.Location as Location
20import SourceSyntax.Location as Location
16import SourceSyntax.Expression
21import SourceSyntax.Expression
17import SourceSyntax.PrettyPrint
22import SourceSyntax.PrettyPrint
18import SourceSyntax.Declaration (Assoc)
23import SourceSyntax.Declaration (Assoc)
19−import Text.Parsec hiding (newline,spaces,State)
20−import Text.Parsec.Indent
22reserveds = [ "if", "then", "else"
25reserveds = [ "if", "then", "else"
23 , "case", "of"
26 , "case", "of"
@@ -49,7 +52,8 @@ jsReserveds = Set.fromList
49expecting = flip (<?>)
52expecting = flip (<?>)
51type OpTable = Map.Map String (Int, Assoc)
54type OpTable = Map.Map String (Int, Assoc)
55+type SourceM = State SourcePos
52−type IParser a = ParsecT String OpTable (State SourcePos) a
56+type IParser a = ParsecT String OpTable SourceM a
54iParse :: IParser a -> String -> Either ParseError a
58iParse :: IParser a -> String -> Either ParseError a
55iParse = iParseWithTable "" Map.empty
59iParse = iParseWithTable "" Map.empty
@@ -58,23 +62,6 @@ iParseWithTable :: SourceName -> OpTable -> IParser a -> String -> Either ParseE
58iParseWithTable sourceName table aParser input =
62iParseWithTable sourceName table aParser input =
59 runIndent sourceName $ runParserT aParser table sourceName input
63 runIndent sourceName $ runParserT aParser table sourceName input
61−readMaybe :: Read a => String -> Maybe a
63− case [ x | (x,t) <- reads s, ("","") <- lex t ] of
67−backslashed :: IParser Char
71− case readMaybe ['\'','\\',c,'\''] of
72− Just chr -> return chr
74− fail $ "Did not recognize character '\\" ++ [c] ++
75− "'. If the backslash is meant to be a character of its own, " ++
76− "it should be escaped like this: \"\\\\" ++ [c] ++ "\""
78var :: IParser String
65var :: IParser String
79var = makeVar (letter <|> char '_' <?> "variable")
66var = makeVar (letter <|> char '_' <?> "variable")
@@ -369,24 +356,73 @@ glSource src =
372−str :: IParser String
359+--str :: IParser String
373−str = choice [ quote >> dewindows <$> manyTill (backslashed <|> anyChar) quote
360+str = expecting "String" $ do
374− , liftM dewindows . expecting "string" . betwixt '"' '"' . many $
361+ s <- choice [ multiStr, singleStr ]
375− backslashed <|> satisfy (/='"')
362+ processAs T.stringLiteral . sandwich '\"' $ concat s
377− where
364+ rawString quote insides =
378− quote = try (string "\"\"\"")
365+ quote >> manyTill insides quote
380− -- Remove \r from strings to fix generated JavaScript
367+ multiStr = rawString (try (string "\"\"\"")) multilineStringChar
381− dewindows [] = []
368+ singleStr = rawString (char '"') stringChar
383− let (pre, suf) = break (`elem` ['\r','\n']) cs
370+ stringChar :: IParser String
384− in pre ++ case suf of
371+ stringChar = choice [ newlineChar, escaped '\"', (:[]) <$> satisfy (/= '\"') ]
385− ('\r':'\n':rest) -> '\n' : dewindows rest
372+
386− ('\n':rest) -> '\n' : dewindows rest
373+ multilineStringChar :: IParser String
387− ('\r':rest) -> '\n' : dewindows rest
374+ multilineStringChar =
388− _ -> []
375+ do noEnd
376+ choice [ newlineChar, escaped '\"', expandQuote <$> anyChar ]
378+ noEnd = notFollowedBy (string "\"\"\"")
379+ expandQuote c = if c == '\"' then "\\\"" else [c]
381+ newlineChar :: IParser String
383+ choice [ char '\n' >> return "\\n"
384+ , char '\r' >> return "\\r" ]
386+sandwich :: Char -> String -> String
387+sandwich delim s = delim : s ++ [delim]
389+escaped :: Char -> IParser String
390+escaped delim = try $ do
392+ c <- char '\\' <|> char delim
390chr :: IParser Char
395chr :: IParser Char
391−chr = betwixt '\'' '\'' (backslashed <|> satisfy (/='\''))
396+chr = betwixt '\'' '\'' character <?> "character"
392− <?> "character"
397+ where
398+ nonQuote = satisfy (/='\'')
400+ c <- choice [ escaped '\''
401+ , (:) <$> char '\\' <*> many1 nonQuote
402+ , (:[]) <$> nonQuote ]
403+ processAs T.charLiteral $ sandwich '\'' c
405+processAs :: (T.GenTokenParser String u SourceM -> IParser a) -> String -> IParser a
406+processAs processor s = calloutParser s (processor lexer)
408+ calloutParser :: String -> IParser a -> IParser a
409+ calloutParser inp p = either (fail . show) return (iParse p inp)
411+ lexer :: T.GenTokenParser String u SourceM
412+ lexer = T.makeTokenParser elmDef
414+ -- I don't know how many of these are necessary for charLiteral/stringLiteral
415+ elmDef :: T.GenLanguageDef String u SourceM
416+ elmDef = T.LanguageDef
417+ { T.commentStart = "{-"
418+ , T.commentEnd = "-}"
419+ , T.commentLine = "--"
420+ , T.nestedComments = True
421+ , T.identStart = undefined
422+ , T.identLetter = undefined
423+ , T.opStart = undefined
424+ , T.opLetter = undefined
425+ , T.reservedNames = reserveds
426+ , T.reservedOpNames = [":", "->", "<-", "|"]
427+ , T.caseSensitive = True