descript-lang-0.2.0.0: src/Descript/Misc/Build/Read/Parse/Loc.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
module Descript.Misc.Build.Read.Parse.Loc
( RangeStream
, runParserFR
, ranged
, expRanged
, expRanged_
, exactlyR
, mapSatisfyR
, rangeToPosStack
, getLoc
, posToLoc
, locToPos
) where
import Descript.Misc.Loc
import Descript.Misc.Ann
import Text.Megaparsec hiding (parse)
import Core.Text.Megaparsec
import Core.Text.Megaparsec.Error
import Data.List
import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
import qualified Data.Text as Text
type RangeStream a = [a Range]
instance (Ann a, Ord (a Range)) => Stream (RangeStream a) where
type Token (RangeStream a) = a Range
type Tokens (RangeStream a) = [a Range]
tokenToChunk _pxy x = [x]
tokensToChunk _pxy = id
chunkToTokens _pxy = id
chunkLength _pxy = length
chunkEmpty _pxy = null
positionAt1 _pxy pos = rePos pos . start . getRange
positionAtN _pxy pos [] = pos
positionAtN _pxy pos (x : _) = rePos pos $ start $ getRange x
advance1 _pxy _ pos = rePos pos . end . getRange
advanceN _pxy _ pos [] = pos
advanceN _pxy _ pos xs = rePos pos $ start $ getRange $ last xs
take1_ = uncons
takeN_ 0 [] = Just ([], [])
takeN_ _ [] = Nothing
takeN_ n xs = Just $ splitAt n xs
takeWhile_ = span
-- | If the parser fails, converts the failure information back into
-- text using the given source so it can be displayed to the user.
runParserFR :: (Ord e, Ann i, Ord (i Range))
=> Parsec e (RangeStream i) o
-> String
-> Text
-> RangeStream i
-> Either (ParseError Char e) o
runParserFR parser filename source input
= case runParserF parser filename input of
Left err -> Left $ traverseErrorSource fixErrorSource err
Right output -> Right output
where fixErrorSource = Text.unpack . (`inText` source) . getRange
-- | All text consumed by the parser will be in the range.
ranged :: (MonadParsec e s m) => m (Range -> a) -> m a
ranged parse = do
start' <- getLoc
f <- parse
end' <- getLoc
let range
= Range
{ start = start'
, end = end'
}
pure $ f range
-- | The range will start where the parser starts, and have the given
-- length in columns and no length in lines.
expRanged :: (MonadParsec e s m)
=> Int -> m (Range -> a) -> m a
expRanged len parse = do
start' <- getLoc
f <- parse
let range
= Range
{ start = start'
, end = start' `addCols` len
}
pure $ f range
-- | The range will start where the parser starts, and have the given
-- length in columns and no length in lines.
expRanged_ :: (MonadParsec e s m)
=> Int -> m () -> m Range
expRanged_ len = expRanged len . (id <$)
-- | Expects the input without the range to be the given value.
exactlyR :: (MonadParsec e s m, Functor a, Token s ~ a Range, Eq (a ()))
=> a () -> m ()
exactlyR = mapExactly remAnns . (singletonRange loc1 <$)
-- | Applies the function to the input without the range. If it
-- succeeds, returns the output. If it fails, returns an error
-- indicating that the input was unexpected.
mapSatisfyR :: (MonadParsec e s m, Functor a, Token s ~ a Range)
=> (a () -> Maybe b) -> m b
mapSatisfyR f = mapSatisfy $ f . remAnns
getLoc :: MonadParsec e s m => m Loc
getLoc = posToLoc <$> getPosition
-- | Converts a range to a stack of positions for a 'ParseError'.
-- The stack will have one item - the start of the range.
-- Uses the string for the filename.
rangeToPosStack :: String -> Range -> NonEmpty SourcePos
rangeToPosStack filename range = locToPos filename (start range) :| []
posToLoc :: SourcePos -> Loc
posToLoc pos
= Loc
{ line = sourceLine pos
, column = sourceColumn pos
}
rePos :: SourcePos -> Loc -> SourcePos
rePos = locToPos . sourceName
locToPos :: String -> Loc -> SourcePos
locToPos filename loc
= SourcePos
{ sourceName = filename
, sourceLine = line loc
, sourceColumn = column loc
}
-- | Alias for 'getAnn' when dealing with a ranged value.
getRange :: (Ann a) => a Range -> Range
getRange = getAnn