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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
{- |
    Module      :  Check.Expression
    Description :  Check and validation for Expression

    This module contains the check function
    and validation functions for Expression.
    The messages are only saved if the check
    is switched ON in the config (`shallCheck Check`)
-}








module Check.Expression (expressionCheck) where

import AST.PositionUtils
import AST.Span          (Pos, start)
import AST.SpanAST
import Check.Types       (CheckF, Message (..))
import Config.ReadConfig (shallCheck)
import Config.Types      (Check (..))
import Utils             (condMsg)

-- ----------------------------------------------------------------------------
-- Check Function
-- ----------------------------------------------------------------------------

-- |Check Expression
expressionCheck :: CheckF Expression
expressionCheck e = case e of
  IfThenElse si i st t se el -> keywords ++ subexpr
    where
      pi = start si
      pt = start st
      pe = start se
      keywords
        = condMsg (shallCheck CIfThenElseKW && not (validITEkw pi pt pe))
                  [Message pi "IfThenElse: Wrong indentation of keywords."]
      subexpr
        = condMsg (shallCheck CIfThenElseSubExpr
                           && not (validITEsubE pi i pt t pe el))
                  [Message pi
                           "IfThenElse: Wrong indentation of subexpressions."]
  Let sl ds si _             -> keywords ++ decl ++ eq
    where pl = start sl
          keywords
            = condMsg (shallCheck CLet && not (validLetKW pl (start si)))
                      [Message pl
                               "Let: Keywords 'let' and 'in' are not aligned."]
          decl
            = condMsg (shallCheck CLet && not (validLetDecl ds))
                      [Message (declPos $ head ds)
                               "Let: Declarations are not aligned."]
          eq
            = condMsg (shallCheck CLet && not (validLetEq ds))
                      [Message (declPos $ head ds)
                               "Let: Equality signs are not aligned."]
  Case _ pc _ _ alts
    | shallCheck CCase && not (validAlts alts) ->
        [Message (start pc) "Case: Arrows are not aligned."]
  _                          -> []

-- ----------------------------------------------------------------------------
-- Validation Functions
-- ----------------------------------------------------------------------------

--- --------------------------------------------------------------------------
--- Validates correct position of `if`, `then` and `else` keywords.
--- The following layouts are considered valid:
---        if ... then ... else ...
---
---        if ... then ...
---               else ...
---
---        if ...
---          then ...
---          else ...

-- |Valid if indentation of keywords is as mentioned above
validITEkw :: Pos -> Pos -> Pos -> Bool
validITEkw pi pt pe =
    (line pi == line pt && line pt == line pe)
 || (line pi == line pt && col pt == col pe)
 || (col pt == ((col pi) + 2) && col pt == col pe)


-- |Valid if space between keywords and subexpressions is as mentioned above
validITEsubE :: Pos -> Expression -> Pos -> Expression -> Pos -> Expression
             -> Bool
validITEsubE pi i pt t pe el =
     (col (exprPos  i) == ((col pi) + 3))
  && (col (exprPos  t) == ((col pt) + 5))
  && (col (exprPos el) == ((col pe) + 5))


--- --------------------------------------------------------------------------
--- Validates correct alignment of a `let` expression.
--- The following layouts are considered valid:
---
---         let decl in expr
---
---         let decl1 = ...
---             decl2 = ...
---             ...
---         in ...
-- |Valid if keywords `let` and `in` are aligned
validLetKW :: Pos -> Pos -> Bool
validLetKW pl pi = (col pl == col pi) || (line pl == line pi)

-- |Valid if declarations are aligned
validLetDecl :: [Decl] -> Bool
validLetDecl ds = (allColEq $ map declPos ds)

-- |Valid if equality signs are aligned
validLetEq :: [Decl] -> Bool
validLetEq ds = (allColEq $ map declPosEq ds)

--- --------------------------------------------------------------------------
--- Validates correct layout of case alternatives.
--- The following layouts are considered valid:
---            case e of E1 -> ...
---                      E2 -> ...
---
---            case e of
---              E1 -> ...
---              E2 -> ...

-- |Valid if alternatives are aligned
validAlts :: [Alt] -> Bool
validAlts alts
  | null alts = True
  | otherwise = allColEq $ map altPos alts