2013-06-14 01:35:37 +00:00
|
|
|
module Parse.Helpers where
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2013-10-17 17:40:25 +00:00
|
|
|
import Prelude hiding (until)
|
2012-06-10 04:21:16 +00:00
|
|
|
import Control.Applicative ((<$>),(<*>))
|
|
|
|
import Control.Monad
|
2012-11-25 04:49:56 +00:00
|
|
|
import Control.Monad.State
|
2013-04-25 05:26:01 +00:00
|
|
|
import Data.Char (isUpper)
|
2013-08-24 22:00:23 +00:00
|
|
|
import qualified Data.Set as Set
|
2013-09-15 20:42:19 +00:00
|
|
|
import qualified Data.Map as Map
|
2013-07-16 19:37:48 +00:00
|
|
|
import SourceSyntax.Helpers as Help
|
2013-06-14 01:35:37 +00:00
|
|
|
import SourceSyntax.Location as Location
|
|
|
|
import SourceSyntax.Expression
|
2013-10-15 02:38:38 +00:00
|
|
|
import SourceSyntax.PrettyPrint
|
2013-09-15 20:42:19 +00:00
|
|
|
import SourceSyntax.Declaration (Assoc)
|
2012-11-25 04:49:56 +00:00
|
|
|
import Text.Parsec hiding (newline,spaces,State)
|
|
|
|
import Text.Parsec.Indent
|
2012-06-10 04:21:16 +00:00
|
|
|
|
|
|
|
reserveds = [ "if", "then", "else"
|
2013-02-04 10:56:22 +00:00
|
|
|
, "case", "of"
|
2012-06-11 13:11:15 +00:00
|
|
|
, "let", "in"
|
2013-02-04 10:56:22 +00:00
|
|
|
, "data", "type"
|
2012-06-11 13:11:15 +00:00
|
|
|
, "module", "where"
|
2013-05-21 08:02:59 +00:00
|
|
|
, "import", "as", "hiding", "open"
|
2012-06-28 08:52:47 +00:00
|
|
|
, "export", "foreign" ]
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2013-08-24 22:00:23 +00:00
|
|
|
jsReserveds :: Set.Set String
|
|
|
|
jsReserveds = Set.fromList
|
|
|
|
[ "null", "undefined", "Nan", "Infinity", "true", "false", "eval"
|
|
|
|
, "arguments", "int", "byte", "char", "goto", "long", "final", "float"
|
|
|
|
, "short", "double", "native", "throws", "boolean", "abstract", "volatile"
|
|
|
|
, "transient", "synchronized", "function", "break", "case", "catch"
|
|
|
|
, "continue", "debugger", "default", "delete", "do", "else", "finally"
|
|
|
|
, "for", "function", "if", "in", "instanceof", "new", "return", "switch"
|
|
|
|
, "this", "throw", "try", "typeof", "var", "void", "while", "with", "class"
|
|
|
|
, "const", "enum", "export", "extends", "import", "super", "implements"
|
|
|
|
, "interface", "let", "package", "private", "protected", "public"
|
|
|
|
, "static", "yield"
|
|
|
|
]
|
|
|
|
|
2012-06-10 04:21:16 +00:00
|
|
|
expecting = flip (<?>)
|
|
|
|
|
2013-09-15 20:42:19 +00:00
|
|
|
type OpTable = Map.Map String (Int, Assoc)
|
|
|
|
type IParser a = ParsecT String OpTable (State SourcePos) a
|
2013-08-29 07:54:54 +00:00
|
|
|
|
2013-09-15 20:42:19 +00:00
|
|
|
iParse :: IParser a -> String -> Either ParseError a
|
|
|
|
iParse = iParseWithTable "" Map.empty
|
|
|
|
|
|
|
|
iParseWithTable :: SourceName -> OpTable -> IParser a -> String -> Either ParseError a
|
|
|
|
iParseWithTable sourceName table aParser input =
|
|
|
|
runIndent sourceName $ runParserT aParser table sourceName input
|
2012-11-25 04:49:56 +00:00
|
|
|
|
2013-12-12 02:57:56 +00:00
|
|
|
readMaybe :: Read a => String -> Maybe a
|
|
|
|
readMaybe s =
|
|
|
|
case [ x | (x,t) <- reads s, ("","") <- lex t ] of
|
|
|
|
[x] -> Just x
|
|
|
|
_ -> Nothing
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
backslashed :: IParser Char
|
2013-11-11 00:20:39 +00:00
|
|
|
backslashed = do
|
|
|
|
char '\\'
|
|
|
|
c <- anyChar
|
|
|
|
case readMaybe ['\'','\\',c,'\''] of
|
|
|
|
Just chr -> return chr
|
|
|
|
Nothing ->
|
|
|
|
fail $ "Did not recognize character '\\" ++ [c] ++
|
|
|
|
"'. If the backslash is meant to be a character of its own, " ++
|
|
|
|
"it should be escaped like this: \"\\\\" ++ [c] ++ "\""
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
var :: IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
var = makeVar (letter <|> char '_' <?> "variable")
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
lowVar :: IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
lowVar = makeVar (lower <?> "lower case variable")
|
2012-11-25 04:49:56 +00:00
|
|
|
capVar :: IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
capVar = makeVar (upper <?> "upper case variable")
|
|
|
|
|
2013-08-01 22:38:19 +00:00
|
|
|
qualifiedVar :: IParser String
|
|
|
|
qualifiedVar = do
|
|
|
|
vars <- many ((++) <$> capVar <*> string ".")
|
|
|
|
(++) (concat vars) <$> lowVar
|
|
|
|
|
2012-12-26 22:07:09 +00:00
|
|
|
rLabel :: IParser String
|
2013-07-29 21:19:16 +00:00
|
|
|
rLabel = lowVar
|
2012-12-26 22:07:09 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
innerVarChar :: IParser Char
|
2012-06-10 04:21:16 +00:00
|
|
|
innerVarChar = alphaNum <|> char '_' <|> char '\'' <?> ""
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
makeVar :: IParser Char -> IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
makeVar p = do v <- (:) <$> p <*> many innerVarChar
|
|
|
|
guard (v `notElem` reserveds)
|
2013-08-31 20:22:00 +00:00
|
|
|
return v
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
reserved :: String -> IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
reserved word =
|
|
|
|
try (string word >> notFollowedBy innerVarChar) >> return word
|
|
|
|
<?> "reserved word '" ++ word ++ "'"
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
anyOp :: IParser String
|
2013-08-01 22:38:19 +00:00
|
|
|
anyOp = betwixt '`' '`' qualifiedVar <|> symOp <?> "infix operator (e.g. +, *, ||)"
|
2012-11-23 03:48:54 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
symOp :: IParser String
|
2013-07-16 19:37:48 +00:00
|
|
|
symOp = do op <- many1 (satisfy Help.isSymbol)
|
2013-02-04 10:56:22 +00:00
|
|
|
guard (op `notElem` [ "=", "..", "->", "--", "|", "\8594", ":" ])
|
2012-12-25 08:39:18 +00:00
|
|
|
case op of
|
|
|
|
"." -> notFollowedBy lower >> return op
|
2013-02-03 10:40:36 +00:00
|
|
|
"\8728" -> return "."
|
2012-12-25 08:39:18 +00:00
|
|
|
_ -> return op
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2013-11-02 18:21:26 +00:00
|
|
|
padded :: IParser a -> IParser a
|
|
|
|
padded p = do whitespace
|
|
|
|
out <- p
|
|
|
|
whitespace
|
|
|
|
return out
|
|
|
|
|
|
|
|
equals :: IParser String
|
|
|
|
equals = string "="
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
arrow :: IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
arrow = string "->" <|> string "\8594" <?> "arrow (->)"
|
|
|
|
|
2013-02-04 10:56:22 +00:00
|
|
|
hasType :: IParser String
|
|
|
|
hasType = string ":" <?> "':' (a type annotation)'"
|
|
|
|
|
2012-06-12 06:28:45 +00:00
|
|
|
|
|
|
|
commitIf check p = commit <|> try p
|
|
|
|
where commit = do (try $ lookAhead check) >> p
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
spaceySepBy1 :: IParser b -> IParser a -> IParser [a]
|
2012-06-12 06:28:45 +00:00
|
|
|
spaceySepBy1 sep p = do
|
|
|
|
a <- p
|
2013-11-02 18:21:26 +00:00
|
|
|
(a:) <$> many (commitIf (whitespace >> sep) (padded sep >> p))
|
2012-06-12 06:28:45 +00:00
|
|
|
|
2012-06-11 13:11:15 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
commaSep1 :: IParser a -> IParser [a]
|
2012-06-12 06:28:45 +00:00
|
|
|
commaSep1 = spaceySepBy1 (char ',' <?> "comma ','")
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
commaSep :: IParser a -> IParser [a]
|
2012-06-12 06:28:45 +00:00
|
|
|
commaSep = option [] . commaSep1
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
semiSep1 :: IParser a -> IParser [a]
|
2012-06-12 06:28:45 +00:00
|
|
|
semiSep1 = spaceySepBy1 (char ';' <?> "semicolon ';'")
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
pipeSep1 :: IParser a -> IParser [a]
|
2012-06-12 06:28:45 +00:00
|
|
|
pipeSep1 = spaceySepBy1 (char '|' <?> "type divider '|'")
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
consSep1 :: IParser a -> IParser [a]
|
2013-02-05 11:19:35 +00:00
|
|
|
consSep1 = spaceySepBy1 (string "::" <?> "cons operator '::'")
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
dotSep1 :: IParser a -> IParser [a]
|
2012-06-11 13:11:15 +00:00
|
|
|
dotSep1 p = (:) <$> p <*> many (try (char '.') >> p)
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
spaceSep1 :: IParser a -> IParser [a]
|
2012-06-10 04:21:16 +00:00
|
|
|
spaceSep1 p = (:) <$> p <*> spacePrefix p
|
|
|
|
|
2013-07-14 15:55:38 +00:00
|
|
|
spacePrefix p = constrainedSpacePrefix p (\_ -> return ())
|
|
|
|
|
|
|
|
constrainedSpacePrefix p constraint =
|
2013-08-14 06:41:44 +00:00
|
|
|
many $ choice [ try (spacing >> lookAhead (oneOf "[({")) >> p
|
|
|
|
, try (spacing >> p)
|
|
|
|
]
|
|
|
|
where
|
|
|
|
spacing = do
|
|
|
|
n <- whitespace
|
|
|
|
constraint n
|
|
|
|
indented
|
2012-06-12 06:28:45 +00:00
|
|
|
|
2013-08-14 06:11:23 +00:00
|
|
|
failure msg = do
|
|
|
|
inp <- getInput
|
|
|
|
setInput ('x':inp)
|
|
|
|
anyToken
|
|
|
|
fail msg
|
|
|
|
|
2012-06-12 06:28:45 +00:00
|
|
|
followedBy a b = do x <- a ; b ; return x
|
2012-06-10 04:21:16 +00:00
|
|
|
|
|
|
|
betwixt a b c = do char a ; out <- c
|
|
|
|
char b <?> "closing '" ++ [b] ++ "'" ; return out
|
|
|
|
|
|
|
|
surround a z name p = do
|
2013-11-02 18:21:26 +00:00
|
|
|
char a ; v <- padded p
|
2012-06-10 04:21:16 +00:00
|
|
|
char z <?> unwords ["closing", name, show z]
|
2012-11-24 21:00:56 +00:00
|
|
|
return v
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
braces :: IParser a -> IParser a
|
2012-06-10 04:21:16 +00:00
|
|
|
braces = surround '[' ']' "brace"
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
parens :: IParser a -> IParser a
|
2012-06-10 04:21:16 +00:00
|
|
|
parens = surround '(' ')' "paren"
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
brackets :: IParser a -> IParser a
|
2012-06-10 04:21:16 +00:00
|
|
|
brackets = surround '{' '}' "bracket"
|
|
|
|
|
2013-10-15 02:38:38 +00:00
|
|
|
addLocation :: (Pretty a) => IParser a -> IParser (Location.Located a)
|
2013-05-29 23:20:38 +00:00
|
|
|
addLocation expr = do
|
2013-10-15 02:38:38 +00:00
|
|
|
(start, e, end) <- located expr
|
|
|
|
return (Location.at start end e)
|
|
|
|
|
|
|
|
located :: IParser a -> IParser (SourcePos, a, SourcePos)
|
|
|
|
located p = do
|
2012-12-25 08:39:18 +00:00
|
|
|
start <- getPosition
|
2013-10-15 02:38:38 +00:00
|
|
|
e <- p
|
2012-12-25 08:39:18 +00:00
|
|
|
end <- getPosition
|
2013-10-15 02:38:38 +00:00
|
|
|
return (start, e, end)
|
2012-12-25 08:39:18 +00:00
|
|
|
|
2013-06-14 03:25:00 +00:00
|
|
|
accessible :: IParser (LExpr t v) -> IParser (LExpr t v)
|
2012-06-10 04:21:16 +00:00
|
|
|
accessible expr = do
|
2012-12-25 08:39:18 +00:00
|
|
|
start <- getPosition
|
2013-07-30 18:43:42 +00:00
|
|
|
ce@(L _ e) <- expr
|
2012-12-26 22:07:09 +00:00
|
|
|
let rest f = do
|
|
|
|
let dot = char '.' >> notFollowedBy (char '.')
|
|
|
|
access <- optionMaybe (try dot <?> "field access (e.g. List.map)")
|
|
|
|
case access of
|
|
|
|
Nothing -> return ce
|
|
|
|
Just _ -> accessible $ do
|
|
|
|
v <- var <?> "field access (e.g. List.map)"
|
|
|
|
end <- getPosition
|
2013-06-14 01:35:37 +00:00
|
|
|
return (Location.at start end (f v))
|
2012-12-26 22:07:09 +00:00
|
|
|
case e of Var (c:cs) | isUpper c -> rest (\v -> Var (c:cs ++ '.':v))
|
|
|
|
| otherwise -> rest (Access ce)
|
|
|
|
_ -> rest (Access ce)
|
2012-06-10 04:21:16 +00:00
|
|
|
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
spaces :: IParser String
|
2013-07-14 15:55:38 +00:00
|
|
|
spaces = concat <$> many1 (multiComment <|> string " ") <?> "spaces"
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2013-07-14 15:55:38 +00:00
|
|
|
forcedWS :: IParser String
|
|
|
|
forcedWS = choice [ try $ (++) <$> spaces <*> (concat <$> many nl_space)
|
|
|
|
, try $ concat <$> many1 nl_space ]
|
|
|
|
where nl_space = try ((++) <$> (concat <$> many1 newline) <*> spaces)
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2013-07-14 12:55:29 +00:00
|
|
|
-- Just eats whitespace until the next meaningful character.
|
2013-07-14 15:55:38 +00:00
|
|
|
dumbWhitespace :: IParser String
|
|
|
|
dumbWhitespace = concat <$> many (spaces <|> newline)
|
2013-07-14 12:55:29 +00:00
|
|
|
|
2013-07-14 15:55:38 +00:00
|
|
|
whitespace :: IParser String
|
|
|
|
whitespace = option "" forcedWS <?> "whitespace"
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
freshLine :: IParser [[String]]
|
2013-02-27 07:33:47 +00:00
|
|
|
freshLine = try (many1 newline >> many space_nl) <|> try (many1 space_nl) <?> ""
|
2012-06-10 04:21:16 +00:00
|
|
|
where space_nl = try $ spaces >> many1 newline
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
newline :: IParser String
|
2013-07-14 15:55:38 +00:00
|
|
|
newline = simpleNewline <|> lineComment <?> "newline"
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
simpleNewline :: IParser String
|
2012-06-10 04:21:16 +00:00
|
|
|
simpleNewline = try (string "\r\n") <|> string "\n"
|
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
lineComment :: IParser String
|
2013-07-14 15:55:38 +00:00
|
|
|
lineComment = do
|
|
|
|
try (string "--")
|
2013-09-05 00:17:48 +00:00
|
|
|
comment <- anyUntil $ simpleNewline <|> (eof >> return "\n")
|
2013-07-14 15:55:38 +00:00
|
|
|
return ("--" ++ comment)
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
multiComment :: IParser String
|
2013-09-05 00:17:48 +00:00
|
|
|
multiComment = (++) <$> try (string "{-") <*> closeComment
|
2012-06-10 04:21:16 +00:00
|
|
|
|
2012-11-25 04:49:56 +00:00
|
|
|
closeComment :: IParser String
|
2013-09-05 00:17:48 +00:00
|
|
|
closeComment =
|
|
|
|
anyUntil . choice $
|
|
|
|
[ try (string "-}") <?> "close comment"
|
|
|
|
, concat <$> sequence [ try (string "{-"), closeComment, closeComment ]
|
|
|
|
]
|
|
|
|
|
2013-10-17 17:40:25 +00:00
|
|
|
until :: IParser a -> IParser b -> IParser b
|
|
|
|
until p end = go
|
|
|
|
where
|
|
|
|
go = end <|> (p >> go)
|
|
|
|
|
2013-09-05 00:17:48 +00:00
|
|
|
anyUntil :: IParser String -> IParser String
|
|
|
|
anyUntil end = go
|
|
|
|
where
|
|
|
|
go = end <|> (:) <$> anyChar <*> go
|
|
|
|
|
2013-10-17 17:40:25 +00:00
|
|
|
ignoreUntil :: IParser a -> IParser (Maybe a)
|
|
|
|
ignoreUntil end = go
|
2013-09-05 00:17:48 +00:00
|
|
|
where
|
|
|
|
ignore p = const () <$> p
|
2013-10-17 17:40:25 +00:00
|
|
|
filler = choice [ ignore multiComment
|
|
|
|
, ignore (markdown (\_ _ -> mzero))
|
|
|
|
, ignore anyChar
|
|
|
|
]
|
|
|
|
go = choice [ Just <$> end
|
|
|
|
, filler `until` choice [ const Nothing <$> eof, newline >> go ]
|
|
|
|
]
|
|
|
|
|
|
|
|
onFreshLines :: (a -> b -> b) -> b -> IParser a -> IParser b
|
|
|
|
onFreshLines insert init thing = go init
|
|
|
|
where
|
|
|
|
go values = do
|
|
|
|
optionValue <- ignoreUntil thing
|
|
|
|
case optionValue of
|
|
|
|
Nothing -> return values
|
|
|
|
Just v -> go (insert v values)
|
2013-09-05 00:17:48 +00:00
|
|
|
|
|
|
|
withSource :: IParser a -> IParser (String, a)
|
|
|
|
withSource p = do
|
|
|
|
start <- getParserState
|
|
|
|
result <- p
|
|
|
|
endPos <- getPosition
|
|
|
|
setParserState start
|
|
|
|
raw <- anyUntilPos endPos
|
|
|
|
return (raw, result)
|
|
|
|
|
|
|
|
anyUntilPos :: SourcePos -> IParser String
|
|
|
|
anyUntilPos pos = go
|
|
|
|
where
|
|
|
|
go = do currentPos <- getPosition
|
|
|
|
case currentPos == pos of
|
|
|
|
True -> return []
|
|
|
|
False -> (:) <$> anyChar <*> go
|
|
|
|
|
2013-10-17 17:40:25 +00:00
|
|
|
markdown :: (String -> [a] -> IParser (String, [a])) -> IParser (String, [a])
|
|
|
|
markdown interpolation = try (string "[markdown|") >> closeMarkdown "" []
|
|
|
|
where
|
|
|
|
closeMarkdown md stuff =
|
|
|
|
choice [ do try (string "|]")
|
|
|
|
return (md, stuff)
|
|
|
|
, uncurry closeMarkdown =<< interpolation md stuff
|
|
|
|
, do c <- anyChar
|
|
|
|
closeMarkdown (md ++ [c]) stuff
|
|
|
|
]
|