|
| 1 | +module Error exposing (DFAErrorType(..), Error(..), contextHasError, errorIcon, errorMenu, machineCheck) |
| 2 | + |
| 3 | +-- This module serves to export checks and exception handling of finite state machines. |
| 4 | +-- When we add support for other machine types, we can extend this module as well. |
| 5 | + |
| 6 | +import Array exposing (Array) |
| 7 | +import Dict exposing (Dict) |
| 8 | +import Environment exposing (Environment) |
| 9 | +import GraphicSVG exposing (..) |
| 10 | +import Helpers exposing (..) |
| 11 | +import Machine exposing (Machine, StateID, TransitionID) |
| 12 | +import Set exposing (Set) |
| 13 | +import SharedModel exposing (..) |
| 14 | +import Tuple exposing (first, second) |
| 15 | + |
| 16 | + |
| 17 | +type Error |
| 18 | + = NoError |
| 19 | + | DFAError DFAErrorType StateID |
| 20 | + | EpsTransError |
| 21 | + | DuplicateStates (Set StateID) |
| 22 | + |
| 23 | + |
| 24 | +type DFAErrorType |
| 25 | + = HasEpsilon |
| 26 | + | Incomplete |
| 27 | + | Nondeterministic |
| 28 | + | Unsure -- Good for debugging? |
| 29 | + |
| 30 | + |
| 31 | +contextHasError : Error -> MachineType -> Bool |
| 32 | +contextHasError err mtype = |
| 33 | + case mtype of |
| 34 | + DFA -> |
| 35 | + if err == NoError then |
| 36 | + False |
| 37 | + |
| 38 | + else |
| 39 | + True |
| 40 | + |
| 41 | + NFA -> |
| 42 | + case err of |
| 43 | + EpsTransError -> |
| 44 | + True |
| 45 | + |
| 46 | + DuplicateStates _ -> |
| 47 | + True |
| 48 | + |
| 49 | + _ -> |
| 50 | + False |
| 51 | + |
| 52 | + |
| 53 | +machineCheck : SharedModel -> Error |
| 54 | +machineCheck sModel = |
| 55 | + let |
| 56 | + mac = |
| 57 | + sModel.machine |
| 58 | + |
| 59 | + tMistakes = |
| 60 | + sModel.machine.transitionMistakes |
| 61 | + |
| 62 | + allTransitionLabels = |
| 63 | + List.sort <| Set.toList <| Set.remove "\\epsilon" <| List.foldr Set.union Set.empty <| Dict.values mac.transitionNames |
| 64 | + |
| 65 | + catch : Maybe (Set String) -> List String |
| 66 | + catch ms = |
| 67 | + case ms of |
| 68 | + Nothing -> |
| 69 | + [] |
| 70 | + |
| 71 | + Just s -> |
| 72 | + Set.toList s |
| 73 | + |
| 74 | + getTrans : Dict TransitionID StateID -> List String |
| 75 | + getTrans d = |
| 76 | + (List.concatMap (\e -> Dict.get e mac.transitionNames |> catch) <| Dict.keys d) |> List.sort |
| 77 | + |
| 78 | + foldingFunc : ( StateID, Dict TransitionID StateID ) -> Error -> Error |
| 79 | + foldingFunc sTuple err = |
| 80 | + case err of |
| 81 | + DFAError errType x -> |
| 82 | + DFAError errType x |
| 83 | + |
| 84 | + NoError -> |
| 85 | + let |
| 86 | + transitions = |
| 87 | + getTrans <| second sTuple |
| 88 | + |
| 89 | + stId = |
| 90 | + first sTuple |
| 91 | + in |
| 92 | + if transitions == allTransitionLabels then |
| 93 | + NoError |
| 94 | + |
| 95 | + else if List.member "\\epsilon" transitions then |
| 96 | + DFAError HasEpsilon stId |
| 97 | + |
| 98 | + else |
| 99 | + case compare (List.length transitions) (List.length allTransitionLabels) of |
| 100 | + LT -> |
| 101 | + DFAError Incomplete stId |
| 102 | + |
| 103 | + EQ -> |
| 104 | + DFAError Incomplete stId |
| 105 | + |
| 106 | + -- e.g. compare [1,1,2] [1,2,3], can be Nondeterministic too |
| 107 | + GT -> |
| 108 | + DFAError Nondeterministic stId |
| 109 | + |
| 110 | + otherErr -> |
| 111 | + otherErr |
| 112 | + in |
| 113 | + if tMistakes /= Nothing then |
| 114 | + EpsTransError |
| 115 | + |
| 116 | + else |
| 117 | + List.foldr (\x acc -> foldingFunc x acc) NoError <| Dict.toList mac.delta |
| 118 | + |
| 119 | + |
| 120 | +errorIcon : Color -> Color -> Shape msg |
| 121 | +errorIcon backclr shapeclrs = |
| 122 | + group |
| 123 | + [ triangle 20 |> filled backclr |> rotate 22.5 |
| 124 | + , roundedRect 7.5 10 5 |> filled shapeclrs |> move ( 0, 7.5 ) |
| 125 | + , circle 3 |> filled shapeclrs |> move ( 0, -2.5 ) |
| 126 | + ] |
| 127 | + |
| 128 | + |
| 129 | +errorMenu : Error -> Machine -> Float -> Float -> Shape msg |
| 130 | +errorMenu err mac winX winY = |
| 131 | + let |
| 132 | + errStId = |
| 133 | + case err of |
| 134 | + DFAError _ stId -> |
| 135 | + case Dict.get stId mac.stateNames of |
| 136 | + Just name -> |
| 137 | + name |
| 138 | + |
| 139 | + Nothing -> |
| 140 | + "" |
| 141 | + |
| 142 | + _ -> |
| 143 | + "" |
| 144 | + |
| 145 | + errorHeader txt = |
| 146 | + group |
| 147 | + [ errorIcon red white |
| 148 | + , text txt |
| 149 | + |> size 20 |
| 150 | + |> fixedwidth |
| 151 | + |> filled darkRed |
| 152 | + |> move ( 20, 0 ) |
| 153 | + ] |
| 154 | + |> scale 0.75 |
| 155 | + |> move ( 0, -20 ) |
| 156 | + |
| 157 | + errorReason = |
| 158 | + group |
| 159 | + [ circle 3 |> filled red |
| 160 | + , (text <| |
| 161 | + case err of |
| 162 | + DFAError HasEpsilon _ -> |
| 163 | + "Possible cause: There are epsilon transitions" |
| 164 | + |
| 165 | + DFAError Incomplete _ -> |
| 166 | + "Possible cause: There are missing transitions" |
| 167 | + |
| 168 | + DFAError Nondeterministic _ -> |
| 169 | + "Possible cause: There are extraneous transitions" |
| 170 | + |
| 171 | + EpsTransError -> |
| 172 | + "Cause: Epsilon transitions are mixed with normal transitions" |
| 173 | + |
| 174 | + _ -> |
| 175 | + "You might have missed something somewhere?" |
| 176 | + ) |
| 177 | + |> size 12 |
| 178 | + |> fixedwidth |
| 179 | + |> filled darkRed |
| 180 | + |> move ( 15, -5 ) |
| 181 | + ] |
| 182 | + |> move ( 0, -40 ) |
| 183 | + |
| 184 | + errorHint = |
| 185 | + group |
| 186 | + [ circle 3 |> filled red |
| 187 | + , (text <| |
| 188 | + case err of |
| 189 | + DFAError HasEpsilon _ -> |
| 190 | + "Hint: Try removing all your epsilon transitions" |
| 191 | + |
| 192 | + DFAError Incomplete _ -> |
| 193 | + "Hint: Check states for missing transitions" |
| 194 | + |
| 195 | + DFAError Nondeterministic _ -> |
| 196 | + "Hint: Find and remove extra transitions" |
| 197 | + |
| 198 | + EpsTransError -> |
| 199 | + "Hint: Switch to Build mode and fix transitions in red" |
| 200 | + |
| 201 | + _ -> |
| 202 | + "" |
| 203 | + ) |
| 204 | + |> size 12 |
| 205 | + |> fixedwidth |
| 206 | + |> filled darkRed |
| 207 | + |> move ( 15, -5 ) |
| 208 | + ] |
| 209 | + |> move ( 0, -60 ) |
| 210 | + |
| 211 | + errorState = |
| 212 | + group |
| 213 | + [ circle 3 |> filled red |
| 214 | + , text "Hint: Check state " |
| 215 | + |> size 12 |
| 216 | + |> fixedwidth |
| 217 | + |> filled darkRed |
| 218 | + |> move ( 15, -5 ) |
| 219 | + , latex 50 12 "blank" errStId AlignLeft |> move ( 150, 3 ) |
| 220 | + ] |
| 221 | + |> move ( 0, -80 ) |
| 222 | + |
| 223 | + actionHint = |
| 224 | + group |
| 225 | + [ circle 3 |> filled red |
| 226 | + , text "Go to Build mode to fix your machine, or use a NFA" |
| 227 | + |> size 12 |
| 228 | + |> fixedwidth |
| 229 | + |> filled darkRed |
| 230 | + |> move ( 15, -5 ) |
| 231 | + ] |
| 232 | + |> move ( 0, -100 ) |
| 233 | + in |
| 234 | + case err of |
| 235 | + DFAError _ _ -> |
| 236 | + group [ errorHeader "DFA error: Your machine has a problem!", errorReason, errorHint, errorState, actionHint ] |
| 237 | + |
| 238 | + EpsTransError -> |
| 239 | + group [ errorHeader "Error: You have invalid state transitions!", errorReason, errorHint ] |
| 240 | + |
| 241 | + NoError -> |
| 242 | + group [] |
| 243 | + |
| 244 | + -- TODO: Add error handling for duplicate state names |
| 245 | + DuplicateStates _ -> |
| 246 | + group [] |
0 commit comments