polyglot-0.1.1.0: src/Parsers/Haskell/Common.hs
module Parsers.Haskell.Common where
import Data.Foldable (Foldable (fold))
import Bookhound.Parser (Parser, check, withTransform)
import Bookhound.ParserCombinators (IsMatch (inverse, is, isNot, noneOf, oneOf),
maybeWithin, someSepBy, within, withinBoth,
(->>-), (<|>), (|*), (|+), (|?))
import Bookhound.Parsers.Char (alpha, alphaNum, char, colon, dot, lower,
newLine, quote, underscore, upper)
import Bookhound.Parsers.Number (double, int)
import Bookhound.Parsers.String (spacing, withinDoubleQuotes, withinParens,
withinQuotes)
import SyntaxTrees.Haskell.Common (Class (..), Ctor (..), CtorOp (..),
Literal (..), Module (..), QClass (QClass),
QCtor (..), QCtorOp (..), QVar (..),
QVarOp (..), Var (..), VarOp (..))
import Utils.Foldable (wrapMaybe)
import Utils.String (wrap, wrapBackQuotes, wrapQuotes)
literal :: Parser Literal
literal = token $
UnitLit <$ is "()"
<|> BoolLit <$> (True <$ is "True" <|> False <$ is "False")
<|> IntLit . show <$> int
<|> FloatLit . show <$> double
<|> CharLit <$> withinQuotes (charLit <|> charLitEscaped)
<|> StringLit <$> withinDoubleQuotes ((stringLit <|> charLitEscaped) |*)
where
charLit = noneOf ['\'', '\\']
charLitEscaped = read . wrapQuotes <$> (is '\\' ->>- alpha)
<|> (is '\\' *> char)
stringLit = noneOf ['"', '\\']
var :: Parser Var
var = Var <$> notReserved
(withinParens (operator opSymbol <|> simpleOperator <|> simpleOperatorFn)
<|> ident lower)
ctor :: Parser Ctor
ctor = Ctor <$> notReserved
(withinParens (operator colon) <|> ident upper)
varOp :: Parser VarOp
varOp = VarOp <$> notReserved
(wrapBackQuotes <$> withinBackQuotes (ident lower)
<|> (operator opSymbol <|> simpleOperator))
ctorOp :: Parser CtorOp
ctorOp = CtorOp <$> notReserved
(wrapBackQuotes <$> withinBackQuotes (ident upper)
<|> operator colon)
class' :: Parser Class
class' = Class <$> ident upper
module' :: Parser Module
module' = Module <$> someSepBy dot (ident upper)
module'' :: Parser Module
module'' = Module <$> someSepBy dot (nonTokenIdent upper)
qVar :: Parser QVar
qVar = uncurry QVar <$> qTerm var
qCtor :: Parser QCtor
qCtor = uncurry QCtor <$> qTerm' Ctor
qVarOp :: Parser QVarOp
qVarOp = uncurry QVarOp <$> qTerm varOp
qCtorOp :: Parser QCtorOp
qCtorOp = uncurry QCtorOp <$> qTerm ctorOp
qClass :: Parser QClass
qClass = uncurry QClass <$> qTerm' Class
ident :: Parser Char -> Parser String
ident start = token $ (:) <$> start <*> (idChar |*)
operator :: Parser Char -> Parser String
operator start = token $ (:) <$> start
<*> ((opSymbol <|> colon) |*)
simpleOperator :: Parser String
simpleOperator = token $ oneOf [":"]
simpleOperatorFn :: Parser String
simpleOperatorFn = token $ oneOf [",", ",,", ",,,"]
nonTokenQVar :: Parser QVar
nonTokenQVar = uncurry QVar <$> qTerm x
where x = Var <$> check "" (`notElem` reservedKeyWords)
(nonTokenIdent lower)
nonTokenIdent :: Parser Char -> Parser String
nonTokenIdent start = (:) <$> start <*> (idChar |*)
idChar :: Parser Char
idChar = alphaNum <|> underscore <|> quote
opSymbol :: Parser Char
opSymbol = oneOf symbolChars
token :: Parser a -> Parser a
token = withTransform $ maybeWithin (anyComment |+) . maybeWithin spacing
qTerm :: Parser a -> Parser (Maybe Module, a)
qTerm x = (,) <$> ((module'' <* dot) |?) <*> x
qTerm' :: (String -> b) -> Parser (Maybe Module, b)
qTerm' fn = token parser
where
parser = do xs <- getComponents <$> module''
pure $ (Module <$> wrapMaybe (init xs), fn $ last xs)
getComponents (Module xs) = xs
anyComment :: Parser String
anyComment = pragma <|> blockComment <|> lineComment
lineComment :: Parser String
lineComment = is "--" ->>- (pure <$> newLine <|>
noneOf symbolChars ->>- (inverse newLine |*))
blockComment :: Parser String
blockComment = wrap "{-" "-}" . fold <$> withinBoth (is "{-") (is "-}")
((:) <$> (isNot "#") <*> ((isNot "-" ->>- isNot "}") |*))
pragma :: Parser String
pragma = wrap "{-#" "#-}" . fold <$> withinBoth (is "{-#") (is "#-}")
((isNot "#" |*))
symbolChars :: [Char]
symbolChars =
['!', '#', '$', '%', '&', '*', '+', '.', '/',
'<', '=', '>', '?', '@', '\\', '|', '^', '|',
'-', '~']
notReserved :: Parser String -> Parser String
notReserved = check "reserved"
(`notElem` (reservedSymbols ++ reservedKeyWords))
reservedKeyWords :: [String]
reservedKeyWords = ["case","class","data","default","deriving",
"do","else","forall" ,"if","import","in",
"infix","infixl","infixr","instance",
"let","module" ,"newtype","of","qualified",
"then","type","where","_" ,"foreign",
"ccall","as","safe","unsafe"]
reservedSymbols :: [String]
reservedSymbols = ["..","::","=","\\","|","<-","->","@","~","=>","[","]"]
withinBackQuotes :: Parser b -> Parser b
withinBackQuotes = within (is '`')