module Parser where
import Text.Parsec
import Text.Parsec.String
import Data.List (find, isSuffixOf)
import Constants
import Types
parseCardFileReference :: CardFileReference -> Sparker [Card]
parseCardFileReference (CardFileReference fp mnr) = do
css <- parseFile fp
case mnr of
Nothing -> return css
Just (CardNameReference cn) ->
case find (\s -> card_name s == cn) css of
Nothing -> throwError $ UnpredictedError $ unwords ["Did't find card", "\"" ++ cn ++ "\"", "in", fp]
Just c -> return [c]
parseFile :: FilePath -> Sparker [Card]
parseFile file = do
str <- liftIO $ readFile file
case parse sparkFile file str of
Left pe -> throwError $ ParseError pe
Right cs -> return cs
--[ Language ]--
sparkFile :: Parser [Card]
sparkFile = do
clean <- eatComments
setInput clean
resetPosition
card `sepEndBy1` whitespace
resetPosition :: Parser ()
resetPosition = do
pos <- getPosition
setPosition $ setSourceColumn (setSourceLine pos 1) 1
getFile :: Parser FilePath
getFile = do
pos <- getPosition
let file = sourceName pos
return file
card :: Parser Card
card = do
whitespace
string keywordCard
whitespace
name <- cardName
whitespace
b <- block
whitespace
fp <- getFile
return $ Card name fp b
cardName :: Parser CardName
cardName = try quotedIdentifier <|> try plainIdentifier <?> "card name"
declarations :: Parser [Declaration]
declarations = (inLineSpace declaration) `sepEndBy` delim
declaration :: Parser Declaration
declaration = choice $ map try
[
block
, alternatives
, sparkOff
, intoDir
, outOfDir
, deploymentKindOverride
, deployment
]
block :: Parser Declaration
block = do
ds <- inBraces $ inWhiteSpace declarations
return $ Block ds
<?> "block"
sparkOff :: Parser Declaration
sparkOff = do
string keywordSpark
linespace
ref <- cardReference
return $ SparkOff ref
<?> "sparkoff"
compilerCardReference :: Parser CompilerCardReference
compilerCardReference = unprefixedCardFileReference
deployerCardReference :: Parser DeployerCardReference
deployerCardReference = goComp <|> goUncomp
where
goComp = compiledCardReference >>= return . DeployerCardCompiled
goUncomp = unprefixedCardFileReference >>= return . DeployerCardUncompiled
compiledCardReference :: Parser CompiledCardReference
compiledCardReference = do
string "compiled"
skip linespace
fp <- filepath
return fp
cardReference :: Parser CardReference
cardReference = try goName <|> try goFile <?> "card reference"
where
goName = cardNameReference >>= return . CardName
goFile = cardFileReference >>= return . CardFile
cardNameReference :: Parser CardNameReference
cardNameReference = do
string keywordCard
linespace
name <- cardName
return $ CardNameReference name
<?> "card name reference"
cardFileReference :: Parser CardFileReference
cardFileReference = do
string keywordFile
skip linespace
unprefixedCardFileReference
unprefixedCardFileReference :: Parser CardFileReference
unprefixedCardFileReference = do
fp <- filepath
linespace
mn <- optionMaybe $ try cardName
return $ case mn of
Nothing -> CardFileReference fp Nothing
Just cn -> CardFileReference fp (Just $ CardNameReference cn)
<?> "card file reference"
intoDir :: Parser Declaration
intoDir = do
string keywordInto
linespace
dir <- directory
return $ IntoDir dir
<?> "into directory declaration"
outOfDir :: Parser Declaration
outOfDir = do
string keywordOutof
linespace
dir <- directory
return $ OutofDir dir
<?> "outof directory declaration"
deploymentKindOverride :: Parser Declaration
deploymentKindOverride = do
string keywordKindOverride
linespace
kind <- try copy <|> link
return $ DeployKindOverride kind
<?> "deployment kind override"
where
copy = string keywordCopy >> return CopyDeployment
link = string keywordLink >> return LinkDeployment
shortDeployment :: Parser Declaration
shortDeployment = do
source <- filepath
return $ Deploy source source Nothing
longDeployment :: Parser Declaration
longDeployment = do
source <- filepath
linespace
kind <- deploymentKind
linespace
dest <- filepath
return $ Deploy source dest kind
deployment :: Parser Declaration
deployment = try longDeployment <|> shortDeployment
<?> "deployment"
deploymentKind :: Parser (Maybe DeploymentKind)
deploymentKind = try link <|> try copy <|> def
<?> "deployment kind"
where
link = string linkKindSymbol >> return (Just LinkDeployment)
copy = string copyKindSymbol >> return (Just CopyDeployment)
def = string unspecifiedKindSymbol >> return Nothing
alternatives :: Parser Declaration
alternatives = do
string keywordAlternatives
linespace
ds <- directory `sepBy1` linespace
return $ Alternatives ds
-- [ FilePaths ]--
filepath :: Parser FilePath
filepath = try quotedIdentifier <|> plainIdentifier
directory :: Parser Directory
directory = do
d <- filepath
return $ if "/" `isSuffixOf` d
then init d
else d
<?> "directory"
--[ Comments ]--
comment :: Parser String
comment = lineComment <|> blockComment <?> "Comments"
lineComment :: Parser String
lineComment = do
skip $ string lineCommentStr
anyChar `manyTill` try eol
blockComment :: Parser String
blockComment = do
skip $ string start
anyChar `manyTill` try (string stop)
where (start, stop) = blockCommentStrs
notComment :: Parser String
notComment = manyTill anyChar (lookAhead ((skip comment) <|> eof))
eatComments :: Parser String
eatComments = do
optional comment
xs <- notComment `sepBy` comment
optional comment
let withoutComments = concat xs
return withoutComments
--[ Identifiers ]--
plainIdentifier :: Parser String
plainIdentifier = many1 $ noneOf $ quotesChar : lineDelimiter ++ whitespaceChars ++ bracesChars
quotedIdentifier :: Parser String
quotedIdentifier = inQuotes $ many $ noneOf $ quotesChar:endOfLineChars
--[ Delimiters ]--
inBraces :: Parser a -> Parser a
inBraces = between (char '{') (char '}')
inQuotes :: Parser a -> Parser a
inQuotes = between (char quotesChar) (char quotesChar)
delim :: Parser String
delim = try (string lineDelimiter) <|> go
where
go = do
e <- eol
ws <- whitespace
return $ e ++ ws
--[ Whitespace ]--
inLineSpace :: Parser a -> Parser a
inLineSpace = between linespace linespace
inWhiteSpace :: Parser a -> Parser a
inWhiteSpace = between whitespace whitespace
linespace :: Parser String
linespace = many $ oneOf linespaceChars
whitespace :: Parser String
whitespace = many $ oneOf whitespaceChars
eol :: Parser String
eol = try (string "\n\r")
<|> try (string "\r\n")
<|> try (string "\n")
<|> string "\r"
<?> "end of line"
--[ Utils ]--
skip :: Parser a -> Parser ()
skip p = p >> return ()