packages feed

project-m36-1.0.0: src/bin/SQL/Interpreter/Base.hs

module SQL.Interpreter.Base where
import ProjectM36.Interpreter
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as Lex
import Data.Text as T (Text, singleton, pack, splitOn, toLower)
import Data.Functor (($>))

-- consumes only horizontal spaces
spaceConsumer :: Parser ()
spaceConsumer = Lex.space space1 (Lex.skipLineComment "--") (Lex.skipBlockComment "{-" "-}")

opChar :: Parser Char
opChar = oneOf (":!#$%&*+./<=>?\\^|-~" :: String)-- remove "@" so it can be used as attribute marker without spaces

-- parse case-insensitive keyword
reserved :: Text -> Parser ()
reserved word = do
  try (string' word *> spaceConsumer)

reserveds :: Text -> Parser ()
reserveds words' = do
  let words'' = T.splitOn " " words'
  reserveds' words''

reserveds' :: [Text] -> Parser ()
reserveds' = mapM_ reserved 
  
-- does not consume trailing spaces
qualifiedNameSegment :: Text -> Parser Text
qualifiedNameSegment sym = T.toLower <$> string' sym

reservedOp :: Text -> Parser ()
reservedOp op = try (spaceConsumer *> string op *> notFollowedBy opChar *> spaceConsumer)

parens :: Parser a -> Parser a
parens = between (symbol "(") (symbol ")")

braces :: Parser a -> Parser a
braces = between (symbol "{") (symbol "}")

identifier :: Parser Text
identifier = do
  istart <- letterChar <|> char '_'
  (toLower <$> identifierRemainder istart) <* spaceConsumer

identifierRemainder :: Char -> Parser Text
identifierRemainder c = do
  rest <- many (alphaNumChar <|> char '_' <|> char '#')
  spaceConsumer
  pure (pack (c:rest))

symbol :: Text -> Parser Text
symbol = Lex.symbol spaceConsumer

comma :: Parser Text
comma = symbol ","

sepByComma1 :: Parser a -> Parser [a]
sepByComma1 p = sepBy1 p comma

sepByComma :: Parser a -> Parser [a]
sepByComma p = sepBy p comma

pipe :: Parser Text
pipe = symbol "|"

semi :: Parser Text
semi = symbol ";"

nline :: Parser Text
nline = (T.singleton <$> newline) <|> crlf

integer :: Parser Integer
integer = Lex.signed (pure ()) Lex.decimal <* spaceConsumer

natural :: Parser Integer
natural = Lex.decimal <* spaceConsumer

double :: Parser Double
double = Lex.float <* spaceConsumer

-- | When an identifier is quoted, it can contain any string.
quotedIdentifier :: Parser Text
quotedIdentifier =
  (T.pack <$> (doubleQuote *> many (escapedDoubleQuote <|> notDoubleQuote) <* doubleQuote)) <* spaceConsumer
  where
    doubleQuote = char '"'
    escapedDoubleQuote = chunk "\"\"" $> '"'
    notDoubleQuote = satisfy ('"' /=)