cparsing-0.1.0.0: Text/Parsec/PosOps.hs
{-# LANGUAGE DeriveDataTypeable #-}
-- | Operations on source positions
module Text.Parsec.PosOps where
import Text.Parsec
import Text.Parsec.Pos
import Data.Function
import Data.Data
import Debug.Trace
-- * Utils on source positions
-- | Shows a source position in a @line:col@ format, omitting file name.
shortShow :: SourcePos -> String
shortShow sp = show (sourceLine sp) ++ ":" ++ show (sourceColumn sp)
-- | Initial position in the same file as
initPosIn :: SourcePos -> SourcePos
initPosIn sp = initialPos (sourceName sp)
takeToPos' sp = takeToPos sp (initPosIn sp)
dropToPos' sp = dropToPos sp (initPosIn sp)
takeToPos :: SourcePos -> SourcePos -> String -> (SourcePos, String)
takeToPos to curr (c:s) | curr < to = let (endPos, res) = takeToPos to (updatePosChar curr c) s
in (endPos, c : res)
takeToPos to curr s = (curr, [])
dropToPos :: SourcePos -> SourcePos -> String -> (SourcePos, String)
dropToPos to curr (c:s) | curr < to = dropToPos to (updatePosChar curr c) s
dropToPos to curr s = (curr, s)
-- * Source Ranges
-- | A range of characters in a source file given by a begin and an end position.
data SourceRange = SourceRange { srcRangeBegin :: SourcePos
, srcRangeEnd :: SourcePos
} deriving (Eq, Ord, Typeable, Data)
instance Show SourceRange where
show sr = show (srcRangeBegin sr) ++ " -- " ++ show (srcRangeEnd sr)
-- | Shows a range in a @line:col-line:col@ format
shortShowRng :: SourceRange -> String
shortShowRng sr = shortShow (srcRangeBegin sr) ++ "-" ++ shortShow (srcRangeEnd sr)
-- | Creates a source range between two source positions
srcRange :: SourcePos -> SourcePos -> SourceRange
srcRange pos0 pos1 = if pos0 < pos1 then SourceRange pos0 pos1
else SourceRange pos1 pos0
-- | True, if a source range is completely inside another range
rangeInside :: SourceRange -> SourceRange -> Bool
(SourceRange from1 to1) `rangeInside` (SourceRange from2 to2)
= from2 <= from1 && to1 <= to2 && from2 < to1
-- | True, if a source range is completely inside another range
rangeStrictInside :: SourceRange -> SourceRange -> Bool
(SourceRange from1 to1) `rangeStrictInside` (SourceRange from2 to2)
= from2 <= from1 && to1 <= to2 && (from2 < from1 || to1 < to2)
-- | True, two source ranges overlap
rangeOverlaps :: SourceRange -> SourceRange -> Bool
(SourceRange from1 to1) `rangeOverlaps` (SourceRange from2 to2)
= (from1 <= from2 && from2 <= to1) || (from1 <= to2 && to2 <= to1)
-- | The smallest range that contains the two given range.
srcRngUnion :: SourceRange -> SourceRange -> SourceRange
srcRngUnion sr1 sr2 = SourceRange ((min `on` srcRangeBegin) sr1 sr2) ((max `on` srcRangeEnd) sr1 sr2)
-- | Returns a range that has the same size as the given range but starts from file position of zero.
rngFromStart :: SourceRange -> SourceRange
rngFromStart rng = rangeRelativelyTo rng (srcRangeBegin rng)
-- | Returns an empty range at the beginning of the given range.
rngStartAsRange :: SourceRange -> SourceRange
rngStartAsRange (SourceRange begin _) = SourceRange begin begin
-- | Returns an empty range at the end of the given range.
rngEndAsRange :: SourceRange -> SourceRange
rngEndAsRange (SourceRange _ end) = SourceRange end end
-- | True, iff the range is empty
emptyRange :: SourceRange -> Bool
emptyRange (SourceRange begin end) = (begin == end)
-- | Addition on source positions.
offsetedBy :: SourcePos -> SourcePos -> SourcePos
sp1 `offsetedBy` sp2
= newPos (sourceName sp1) (sourceLine sp1 + sourceLine sp2 - 1) (if sourceLine sp2 == 1 then sourceColumn sp1 + sourceColumn sp2 - 1 else sourceColumn sp2)
-- | Substraction on source positions.
relativelyTo :: SourcePos -> SourcePos -> SourcePos
sp1 `relativelyTo` sp2
= let newLine = sourceLine sp1 - sourceLine sp2 + 1
in newPos (sourceName sp1) newLine (if newLine == 1 then sourceColumn sp1 - sourceColumn sp2 + 1 else sourceColumn sp1)
-- | Gets a source range, with a specified position substracted from beginning and end.
rangeRelativelyTo :: SourceRange -> SourcePos -> SourceRange
sr `rangeRelativelyTo` sp = rangeMapBoth (`relativelyTo` sp) sr
-- | Applies a function on source positions to both begin and end of range
rangeMapBoth :: (SourcePos -> SourcePos) -> SourceRange -> SourceRange
rangeMapBoth f (SourceRange sp0 sp1) = SourceRange (f sp0) (f sp1)
-- | Takes a substring indicated by a source range from the contents of the file
takeSourceRange :: SourceRange -> String -> String
takeSourceRange (SourceRange from to) s
= let init = (initialPos (sourceName from))
(startPos,startsAtPos) = dropToPos from init s
in snd $ takeToPos to startPos startsAtPos