@@ -1,19 +1,21 @@
1module Parse.Helpers where
1module Parse.Helpers where
3import Prelude hiding (until)
3import Prelude hiding (until)
4−import Control.Applicative ((<$>),(<*>))
4+import Control.Applicative ((<$>),(<*>), (<$), pure)
5import Control.Monad
5import Control.Monad
6import Control.Monad.State
6import Control.Monad.State
7import Data.Char (isUpper)
7import Data.Char (isUpper)
8import qualified Data.Set as Set
8import qualified Data.Set as Set
9import qualified Data.Map as Map
9import qualified Data.Map as Map
10+import Text.Parsec hiding (newline,spaces,State)
11+import Text.Parsec.Indent
12+import qualified Text.Parsec.Token as P
10import SourceSyntax.Helpers as Help
14import SourceSyntax.Helpers as Help
11import SourceSyntax.Location as Location
15import SourceSyntax.Location as Location
12import SourceSyntax.Expression
16import SourceSyntax.Expression
13import SourceSyntax.PrettyPrint
17import SourceSyntax.PrettyPrint
14import SourceSyntax.Declaration (Assoc)
18import SourceSyntax.Declaration (Assoc)
15−import Text.Parsec hiding (newline,spaces,State)
16−import Text.Parsec.Indent
18reserveds = [ "if", "then", "else"
20reserveds = [ "if", "then", "else"
19 , "case", "of"
21 , "case", "of"
@@ -45,7 +47,8 @@ jsReserveds = Set.fromList
45expecting = flip (<?>)
47expecting = flip (<?>)
47type OpTable = Map.Map String (Int, Assoc)
49type OpTable = Map.Map String (Int, Assoc)
50+type SourceM = State SourcePos
48−type IParser a = ParsecT String OpTable (State SourcePos) a
51+type IParser a = ParsecT String OpTable SourceM a
50iParse :: IParser a -> String -> Either ParseError a
53iParse :: IParser a -> String -> Either ParseError a
51iParse = iParseWithTable "" Map.empty
54iParse = iParseWithTable "" Map.empty
@@ -54,23 +57,6 @@ iParseWithTable :: SourceName -> OpTable -> IParser a -> String -> Either ParseE
54iParseWithTable sourceName table aParser input =
57iParseWithTable sourceName table aParser input =
55 runIndent sourceName $ runParserT aParser table sourceName input
58 runIndent sourceName $ runParserT aParser table sourceName input
57−readMaybe :: Read a => String -> Maybe a
59− case [ x | (x,t) <- reads s, ("","") <- lex t ] of
63−backslashed :: IParser Char
67− case readMaybe ['\'','\\',c,'\''] of
68− Just chr -> return chr
70− fail $ "Did not recognize character '\\" ++ [c] ++
71− "'. If the backslash is meant to be a character of its own, " ++
72− "it should be escaped like this: \"\\\\" ++ [c] ++ "\""
74var :: IParser String
60var :: IParser String
75var = makeVar (letter <|> char '_' <?> "variable")
61var = makeVar (letter <|> char '_' <?> "variable")
@@ -326,23 +312,69 @@ markdown interpolation = try (string "[markdown|") >> closeMarkdown "" []
328str :: IParser String
314str :: IParser String
329−str = choice [ quote >> dewindows <$> manyTill (backslashed <|> anyChar) quote
315+str = expecting "String" $
330− , liftM dewindows . expecting "string" . betwixt '"' '"' . many $
316+ choice [ quote >> manyTill multilineStringChar quote
331− backslashed <|> satisfy (/='"')
317+ , char '"' >> manyTill stringChar (char '"')
333− where
319+ >>= parseStringLiteral . wrapQuotes '\"' . join
334− quote = try (string "\"\"\"")
320+ where quote = try (string "\"\"\"")
336− -- Remove \r from strings to fix generated JavaScript
322+wrapQuotes :: Char -> String -> String
337− dewindows [] = []
323+wrapQuotes delim s = (delim:s ++ [delim])
339− let (pre, suf) = break (`elem` ['\r','\n']) cs
325+stringChar :: IParser String
340− in pre ++ case suf of
326+stringChar = newlineChar <|> escaped '\"' <|> (pure <$> satisfy (/= '\"'))
341− ('\r':'\n':rest) -> '\n' : dewindows rest
327+
342− ('\n':rest) -> '\n' : dewindows rest
328+multilineStringChar :: IParser String
343− ('\r':rest) -> '\n' : dewindows rest
329+multilineStringChar = noEnd
344− _ -> []
330+ >> (newlineChar <|> escaped '\"' <|> expandQuote <$> anyChar)
331+ where noEnd = notFollowedBy (string "\"\"\"")
332+ expandQuote c = if c == '\"'
336+newlineChar :: IParser String
337+newlineChar = ['\\', 'n'] <$ char '\n'
338+ <|> ['\\', 'r'] <$ char '\r'
340+escaped :: Char -> IParser String
341+escaped delim = try $ do
343+ c <- char '\\' <|> char delim
346chr :: IParser Char
346chr :: IParser Char
347−chr = betwixt '\'' '\'' (backslashed <|> satisfy (/='\''))
347+chr = (betwixt '\'' '\''
348+ (many1 (escaped '\'' <|> (pure <$> satisfy (/='\''))))
349+ >>= parseCharLiteral . wrapQuotes '\'' . join
348− <?> "character"
351+ <?> "character"
353+calloutParser :: String -> IParser a -> IParser a
354+calloutParser inp p = case iParse p inp of
355+ Left err -> fail . show $ err
358+parseStringLiteral :: String -> IParser String
359+parseStringLiteral s = calloutParser s . P.stringLiteral $ lexer
361+parseCharLiteral :: String -> IParser Char
362+parseCharLiteral s = calloutParser s . P.charLiteral $ lexer
364+lexer = P.makeTokenParser elmDef
366+-- I don't know how many of these are necessary for charLiteral/stringLiteral
367+elmDef :: P.GenLanguageDef String u SourceM
368+elmDef = P.LanguageDef {
369+ P.commentStart = "{-"
370+ , P.commentEnd = "-}"
371+ , P.commentLine = "--"
372+ , P.nestedComments = True
373+ , P.identStart = undefined
374+ , P.identLetter = undefined
375+ , P.opStart = undefined
376+ , P.opLetter = undefined
377+ , P.reservedNames = reserveds
378+ , P.reservedOpNames = [":", "->", "<-", "|"]
379+ , P.caseSensitive = True