aboutsummaryrefslogtreecommitdiffhomepage
path: root/scaffolder/src/Text/Edifact/Scaffolder/Messages/Specification.hs
blob: b1e5c2ad25df00080a8658c364322c35c97e7871 (plain) (blame)
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
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Text.Edifact.Scaffolder.Messages.Specification
  ( -- *
    specificationParser
  , messageNameParser
  , listSegments
  ) where

import           Text.Edifact.Scaffolder.Commons
import           Text.Edifact.Scaffolder.Messages.Types

import           Data.Maybe                             (mapMaybe)
import           Text.Parsec

type Parser = Parsec String GroupTrail

newtype GroupTrail = GroupTrail [GroupCode]
  deriving stock Show
  deriving newtype (Semigroup, Monoid)

messageNameParser :: Parser MessageCode
messageNameParser = scanUntil [
    manyTill anyChar (string "Message Type : ") >> MessageCode <$> count 6 upper
  ]

specificationParser :: Parser [Dependency]
specificationParser =
  let scanElements = scan [ segmentInLine segmentElementParser
                          , groupInLine   groupStartElementParser
                          ]
  in interpretDependencies <$> scanElements <?> "Messages specification"

listSegments :: Parser [SegmentCode]
listSegments = mapMaybe (getSegment . getElement) <$> specificationParser

interpretDependencies :: [Element] -> [Dependency]
interpretDependencies = fmap Dependency

groupInLine :: Parser a -> Parser [a]
groupInLine p = single (many (string " ") *> p <* countClosingGroups)

countClosingGroups :: Parser Int
countClosingGroups =
  let parser = many1 (char '-')
            *> many1 (char '+')
           <*  many  (char '|')
  in length <$> parser

closingGroupTrail :: Parser [Element]
closingGroupTrail =
  let groupEndParser = GroupEnd <$> popFromTrail
  in countClosingGroups >>= flip count groupEndParser

groupStartElementParser :: Parser Element
groupStartElementParser =
  let parseStart pos code rep = GroupStart code (Positional pos rep)
  in parseStart <$> positionParser
                <*  many1 (choice [ () <$ try (oneOf "+*#|X "), () <$ try (string "- ") ])
                <*> groupCodeParser
                <*  many1 (char ' ')
                <*> repetitionParser
                <?> "GroupElement"

groupCodeParser :: Parser GroupCode
groupCodeParser =
  let parser = manyTill (char '-') (try $ string "-- Segment group")
            *> many1 (char ' ')
            *> many1 digit
           <*  many1 space
           <*  many1 (char '-')
      group = GroupCode <$> parser
  in group >>= appendToTrail <?> "GroupCodeParser"

appendToTrail :: GroupCode -> Parser GroupCode
appendToTrail code =
  let append (GroupTrail trail) = GroupTrail (code : trail)
  in code <$ modifyState append

popFromTrail :: Parser GroupCode
popFromTrail = do
  previous <- getState
  case previous of
    GroupTrail (current : trail) -> current <$ putState (GroupTrail trail)
    GroupTrail []                -> unexpected "GroupEnd, when state is currently clear"

segmentTrail :: Parser [a]
segmentTrail = [] <$ (many1 (char ' ') <* many (char '|'))

segmentInLine :: Parser Element -> Parser [Element]
segmentInLine p = do
  segment <- many (string " ") *> p
  trail <- choice [ try closingGroupTrail
                  , try segmentTrail
                  ]
  pure (segment : trail)

repetitionParser :: Parser Repetition
repetitionParser =
  Repetition <$> presenceParser
             <*  many1 (string " ")
             <*> cardinalityParser
             <?> "Repetition"

positionParser :: Parser Position
positionParser =
  fromString <$> many1 digit
             <?> "Position"

segmentElementParser :: Parser Element
segmentElementParser =
  let parseSegment pos code rep = Segment code (Positional pos rep)
  in parseSegment <$> positionParser
                  <*  many1 (oneOf "+*#|-X ")
                  <*> segmentCodeParser
                  <*  many1 (string " ")
                  <*  stringToPresenceParser
                  <*  many1 (string " ")
                  <*> repetitionParser
                  <?> "SegmentElement"

segmentCodeParser :: Parser SegmentCode
segmentCodeParser =
  fromString <$> count 3 upper
             <?> "SegmentCode"

cardinalityParser :: Parser Cardinality
cardinalityParser = Cardinality . read <$> many1 digit