stim-parser-0.2.0.0: src/StimParser/ParseUtils.hs
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use <$>" #-}
{-# HLINT ignore "Use <&>" #-}
module StimParser.ParseUtils where
import Data.Void
import Text.Megaparsec hiding (State)
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
-- import System.IO
import Control.Applicative
import GHC.Stack (HasCallStack)
import Control.Monad.State.Lazy
import qualified Data.HashMap.Strict as HS
import qualified Data.Set as Set
import Data.Maybe
import Data.Char (toLower)
type Parser = Parsec Void String
-- type Parser = ParsecT Void String (State Env)
sc :: Parser ()
sc = L.space
space1
(L.skipLineComment "#")
(L.skipBlockComment "(*" "*)")
-- cosume all the white spaces following a Parser
-- white spaces include: " " "\n" "\t"
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
lstring :: String -> Parser String
lstring = lexeme . string
-- | Case-insensitive string parser (custom implementation)
stringCI :: String -> Parser String
stringCI s = try $ mapM charCI s
where
charCI c = do
x <- satisfy (\x -> toLower x == toLower c)
return x -- Return the actual matched character, not the expected one
-- | Case-insensitive lexeme string parser
lstringCI :: String -> Parser String
lstringCI = lexeme . stringCI
safeManyTill :: MonadParsec e s f => f a -> f b -> f [a]
safeManyTill p end = go
where
go = try ([] <$ end) <|> liftA2 (:) p go
parseExhaust :: Parser a -> Parser [a]
parseExhaust parseElm = safeManyTill parseElm (notFollowedBy parseElm)
manyBetween :: Parser a -> Parser a -> Parser String
manyBetween s e = s *> safeManyTill L.charLiteral e
run :: HasCallStack => Parser a -> String -> a
run p s = case m of
Left bundle -> error (errorBundlePretty bundle)
Right r -> r
where
m = runParser p "" s
parseInt :: Parser Int
parseInt = lexeme $ L.signed sc L.decimal
parseFloat :: Parser Double
parseFloat = lexeme $ L.signed sc L.float
-- | Parse a number that may be an integer or a float.
-- This is useful for coordinates and other contexts where integers
-- like @0@ or @1@ should be accepted as @0.0@ or @1.0@.
parseNumber :: Parser Double
parseNumber = lexeme $ L.signed sc (try L.float <|> fromIntegral <$> L.decimal)
parseTuple :: Parser a -> Parser [a]
parseTuple pm = do
lstring "("
let
parseE = do
f <- pm
lstring ","
return f
parseTuple_ = (++) <$> parseExhaust parseE <*> ((: []) <$> pm)
phs <- parseTuple_
lstring ")"
return phs
parseTupleFloat :: Parser [Double]
parseTupleFloat = parseTuple parseFloat
-- | Parse a tuple of numbers (integers or floats).
-- Used for DEM coordinates where bare integers like @(0, 0)@ are common.
parseTupleNumber :: Parser [Double]
parseTupleNumber = parseTuple parseNumber
parseVar :: Parser String
parseVar = do
lexeme $ safeManyTill L.charLiteral (lookAhead (try space1 <|> eof))
excludePredict :: Parser a -> Parser ()
excludePredict p = lookAhead $ notFollowedBy p