packages feed

spade-0.1.0.0: src/Parser/Lib.hs

module Parser.Lib where

import Control.Applicative
import Control.Monad
import Control.Monad.IO.Class
import Data.Text as T

import Common
import Parser.Parser

runParserEither :: forall s a m. (Show s, HaveLocation s, HasEmpty s, MonadIO m) => ParserM m s a -> s -> m (Either (ParseErrorWithParsed a) a)
runParserEither (ParserM _ fn) s = do
  (r, rst) <- fn s
  case r of
    Right res ->
      if isEmpty rst then pure $ Right res else pure $ Left $ ParseErrorWithParsed (Just res) (getLocation rst) (FatalError IncompleteParse)
    Left err -> pure $ Left $ ParseErrorWithParsed Nothing (getLocation rst) err

runParser :: forall a s m. (Show s, HaveLocation s, HasEmpty s, MonadIO m) => ParserM m s a -> s -> m (Maybe a)
runParser p s = runParserEither p s >>= \case
  Right a -> pure $ Just a
  Left _  -> pure Nothing

incLine :: Int -> Parser ()
incLine lc = ParserM "" $ \s ->  pure (Right (), s { twLocation = moveLines (twLocation s) lc })

lookAhead :: Monad m => ParserM m s a -> ParserM m s (Maybe a)
lookAhead (ParserM name fn) = ParserM ("lookahead for (" <> name <> ")") $ \s ->
  fn s >>= \case
    (Right a, _) -> pure (Right $ Just a, s)
    (_, _)     -> pure (Right Nothing, s)

eof :: (HasEof s, Monad m) => ParserM m s ()
eof = ParserM "EOF" $ \s -> case isEof s of
  True  -> pure (Right (), s)
  False -> pure (Left CantHandle, s)

noteof :: (HasEof s, Monad m) => ParserM m s ()
noteof = ParserM "NOT_EOF" $ \s -> case isEof s of
  True -> pure (Left CantHandle, s)
  False  -> pure (Right (), s)

pAny :: (Char -> Bool) -> Parser Char
pAny fn = ParserM "" (\t ->
    pure $ case T.uncons $ twText t of
      Just (h, rst) -> if (fn h)
        then (Right h, t { twText = rst, twLocation = moveCols (twLocation t) 1 })
        else (Left CantHandle, t)
      Nothing -> (Left CantHandle, t)
  )

pChar :: Char -> Parser Char
pChar c = pAny (== c)

pText :: Text -> Parser Text
pText l = ParserM l (\t ->
  pure $ if isPrefixOf l $ twText t
    then let tLen = T.length l
      in (Right l, t { twText = T.drop tLen $ twText t, twLocation = moveCols (twLocation t) tLen})
    else (Left CantHandle, t))

parseAndReturn :: Text -> a -> Parser a
parseAndReturn t a = do
  void $ pText t
  pure a

testParser :: Parser [Char]
testParser = do
  a <- pChar 'a'
  b <- pChar 'b'
  pure [a, b]

cantHandle :: Monad m => ParserM m s a
cantHandle = ParserM "" (\s -> pure (Left CantHandle, s))

nameParser :: Text -> ParserM m s a -> ParserM m s a
nameParser name (ParserM _ f) = ParserM name f

class HasInnerParseable a where
  type InnerToken a
  assemble :: InnerToken a -> Location -> Int -> a

class HasParser a where
  parser :: Parser a

instance HasParser a => HasParser [a] where
  parser = many parser

instance {-# OVERLAPPABLE #-} (HasInnerParseable a, ToSource (InnerToken a), HasParser (InnerToken a)) => HasParser a where
  parser = do
    lc <- getParserLocation
    tr <- parser @(InnerToken a)
    let tLength = (T.length $ toSource tr)
    incOffset tLength
    pure $ assemble tr lc (lcOffset lc + tLength - 1)
    where
      incOffset :: Int -> Parser ()
      incOffset size = ParserM "" $ \s -> let
        location = twLocation s
        currentOffset = lcOffset location
        in pure (Right (), s { twLocation =  location { lcOffset = currentOffset + size }} )