1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
{- |
     Author  : Kai-Oliver Prott
     Version : October 2024

     Datatype and operations to handle Spans.
-}





module Curry.Span (
 Span(..),
 -- * Transformer,
 isSpan, isNoSpan, fromPosition, stripStart, span2Pos, combineSpans, addSpan,
 -- * Distance management
 vertDist, isAfter, isBefore, isBeforeList, spanLength
) where

import Curry.Position

data Span
  = Span
    { start :: Position
    , end   :: Position
    }
  | NoSpan
    deriving (Eq, Show, Read)

isSpan :: Span -> Bool
isSpan (Span {})  = True
isSpan NoSpan     = False

isNoSpan :: Span -> Bool
isNoSpan (Span {})  = False
isNoSpan NoSpan     = True

-- | Create a Span with the given Position as start and end
fromPosition :: Position -> Span
fromPosition NoPos            = NoSpan
fromPosition p@(Position _ _) = Span p p

-- | Sets the start position of a Span to its end position
stripStart :: Span -> Span
stripStart = fromPosition . end

-- | Computes a "vertical distance" between two spans.
-- It is either the row distance of the start end end positions or
-- zero, if the spans overlap.
vertDist :: Span -> Span -> Int
vertDist NoSpan       NoSpan       = 0
vertDist NoSpan       (Span {})    = 0
vertDist (Span {})    NoSpan       = 0
vertDist (Span s1 e1) (Span s2 e2) =
  case rowDist e1 s2 of
    x | x >= 0    -> x
      | e1 <= e2  -> 0 -- they overlap
      | otherwise -> - (rowDist e2 s1)

-- | Checks if the first span is completely after the second span.
isAfter :: Span -> Span -> Bool
isAfter NoSpan     NoSpan     = False
isAfter (Span {})  NoSpan     = False
isAfter NoSpan     (Span {})  = False
isAfter (Span s _) (Span _ e) = s >= e

-- | Checks if the first span is completely before the second span.
isBefore :: Span -> Span -> Bool
isBefore NoSpan     NoSpan     = False
isBefore (Span {})  NoSpan     = False
isBefore NoSpan     (Span {})  = False
isBefore (Span _ e) (Span s _) = e <= s

-- | Checks if the first span is completely
--   before the first span of a list.
--   Reurns `True` if the list is empty
isBeforeList :: Span -> [Span] -> Bool
isBeforeList _   []      = True
isBeforeList sp1 (sp2:_) = isBefore sp1 sp2

span2Pos :: Span -> Position
span2Pos (Span p _) = p
span2Pos NoSpan     = NoPos

combineSpans :: Span -> Span -> Span
combineSpans sp1 sp2 = Span s e
  where s = start sp1
        e = end sp2

addSpan :: Span -> (a, [Span]) -> (a, [Span])
addSpan sp (a, ss) = (a, sp:ss)

spanLength :: Span -> (Int, Int)
spanLength sp = case sp of
  Span (Position x1 y1) (Position x2 y2)
    -> (x2-x1, y2-y1)
  _ -> (0, 0)