In [27]:
{-# FlexibleContexts #-}

import qualified Data.Map as Map
import Text.Parsec

data Signal
  = AD02 | AD11 | AH11 | AM01 |
    AO01 | AR01 | AS01 | AT11 | BR01 |
    CA01 | CC11 | CD01 | CH01 | CI01 |
    CL11 | CL13 | CO01 | CORR | CP11 |
    CR01 | CR02 | CS11 | DA01 | DB01 |
    DC11 | DI01 | DR01 | ED01 | EF01 |
    EM11 | END1 | ERRC | ERRT | FA11 |
    FI01 | FT01 | GC01 | ID01 | ID02 |
    IN11 | IN12 | LK11 | MC01 | MI01 |
    ML01 | ML02 | MT01 | NM11 | NU01 |
    OB01 | OB02 | PD01 | PH01 | PH02 |
    PH03 | PH04 | PI11 | PN01 | PN02 |
    PP11 | PR11 | PS01 | PS02 | QH01 |
    RE01 | RR01 | SA11 | SC01 | SD01 |
    SH01 | SH02 | SH03 | SH04 | SH05 |
    SH06 | SM11 | SV01 | SV02 | SV03 |
    TA01 | TC01 | TM11 | TR11 | TR12 |
    TR13 | TT01 | TU4E | TU4R | TX01 |
    UA11 | UF11 | VH01 | VS01 | WS01 |
    YI01 | ZC01
  deriving (Show, Enum, Ord, Eq, Read)

allSignals :: [String]
allSignals = map show [AD02 ..]
Pragmas of type FlexibleContexts
are not supported.

We need to define a parser which can pull out matches to our Signals

In [28]:
type Parser = Parsec String ()

signalParser :: Parser String
signalParser = choice $ fmap try $ string <$> allSignals

choice is equivalent to the infix <|>

In [29]:
toSignal :: String -> Signal
toSignal s = read s :: Signal

anySignal :: Parser (Signal, String)
anySignal = do
  signal <- signalParser
  content <- manyTill anyToken signalParser
  return (toSignal signal, content)
In [30]:
parse anySignal "" "AD03"
Left (line 1, column 1):
unexpected "3"
expecting "AD02", "AD11", "AH11", "AM01", "AO01", "AR01", "AS01", "AT11", "BR01", "CA01", "CC11", "CD01", "CH01", "CI01", "CL11", "CL13", "CO01", "CORR", "CP11", "CR01", "CR02", "CS11", "DA01", "DB01", "DC11", "DI01", "DR01", "ED01", "EF01", "EM11", "END1", "ERRC", "ERRT", "FA11", "FI01", "FT01", "GC01", "ID01", "ID02", "IN11", "IN12", "LK11", "MC01", "MI01", "ML01", "ML02", "MT01", "NM11", "NU01", "OB01", "OB02", "PD01", "PH01", "PH02", "PH03", "PH04", "PI11", "PN01", "PN02", "PP11", "PR11", "PS01", "PS02", "QH01", "RE01", "RR01", "SA11", "SC01", "SD01", "SH01", "SH02", "SH03", "SH04", "SH05", "SH06", "SM11", "SV01", "SV02", "SV03", "TA01", "TC01", "TM11", "TR11", "TR12", "TR13", "TT01", "TU4E", "TU4R", "TX01", "UA11", "UF11", "VH01", "VS01", "WS01", "YI01" or "ZC01"
In [31]:
p :: String -> (Signal, String)
p input = case parse anySignal "" input of
  Left err -> error $ show err
  Right r -> r

p  "AD11asdf"
(line 1, column 5):
unexpected end of input
expecting "AD02", "AD11", "AH11", "AM01", "AO01", "AR01", "AS01", "AT11", "BR01", "CA01", "CC11", "CD01", "CH01", "CI01", "CL11", "CL13", "CO01", "CORR", "CP11", "CR01", "CR02", "CS11", "DA01", "DB01", "DC11", "DI01", "DR01", "ED01", "EF01", "EM11", "END1", "ERRC", "ERRT", "FA11", "FI01", "FT01", "GC01", "ID01", "ID02", "IN11", "IN12", "LK11", "MC01", "MI01", "ML01", "ML02", "MT01", "NM11", "NU01", "OB01", "OB02", "PD01", "PH01", "PH02", "PH03", "PH04", "PI11", "PN01", "PN02", "PP11", "PR11", "PS01", "PS02", "QH01", "RE01", "RR01", "SA11", "SC01", "SD01", "SH01", "SH02", "SH03", "SH04", "SH05", "SH06", "SM11", "SV01", "SV02", "SV03", "TA01", "TC01", "TM11", "TR11", "TR12", "TR13", "TT01", "TU4E", "TU4R", "TX01", "UA11", "UF11", "VH01", "VS01", "WS01", "YI01" or "ZC01"

Let's look back at the anySignal parser and add an eol check.

In [43]:
anySignal' :: Parser (Signal, String)
anySignal' = do
  signal <- signalParser
  content <- manyTill anyToken (endOfLineOrInput <|> signalLookahead)
  return (toSignal signal, content)

eol :: Parser ()
eol = char '\n' *> return ()

endOfLineOrInput :: Parser ()
endOfLineOrInput = eol <|> eof

signalLookahead = lookAhead signalParser *> return ()

p :: String -> Either ParseError (Signal, String)
p input = parse anySignal' "" input

p  "AD11asdfa sdfaf UA11 some stuff"
Right (AD11,"asdfa sdfaf ")

To avoid eating up the next signal, we use a lookahead in conjunction with the eol | eof logic

Oh noes! what happened to the rest of the signals? We need to use many to parse out more matches

In [45]:
p :: String -> [(Signal, String)]
p input = case parse (many anySignal') "" input of
  Left err -> error $ show err
  Right r -> r

Map.fromList $ p  "AD11asdfa sdfaf UA11 some stuffEM11asdfa      dsfgqergds dfgsdg"
fromList [(AD11,"asdfa sdfaf "),(EM11,"asdfa      dsfgqergds dfgsdg"),(UA11," some stuff")]

Now our data is in a format we can easily work with.