content: {-# LANGUAGE LambdaCase #-}
  
  module BNFC.Backend.C.RegToFlex (printRegFlex) where
  
  -- modified from pretty-printer generated by the BNF converter
  
  import Data.Char (ord, showLitChar)
  import qualified Data.List as List
  
  import BNFC.Abs
  import BNFC.Backend.Common (flexEps)
  
  -- the top-level printing method
  printRegFlex :: Reg -> String
  printRegFlex = render . prt 0
  
  -- you may want to change render and parenth
  
  render :: [String] -> String
  render = rend (0::Int) where
    rend i ss = case ss of
      "["      :ts -> cons "["  $ rend i ts
      "("      :ts -> cons "("  $ rend i ts
      t  : "," :ts -> cons t    $ space "," $ rend i ts
      t  : ")" :ts -> cons t    $ cons ")"  $ rend i ts
      t  : "]" :ts -> cons t    $ cons "]"  $ rend i ts
      t        :ts -> space t   $ rend i ts
      _            -> ""
    cons s t  = s ++ t
    space t s = if null s then t else t ++ s
  
  parenth :: [String] -> [String]
  parenth ss = ["("] ++ ss ++ [")"]
  
  -- the printer class does the job
  class Print a where
    prt :: Int -> a -> [String]
  
  prPrec :: Int -> Int -> [String] -> [String]
  prPrec i j = if j<i then parenth else id
  
  instance Print Identifier where
    prt _ (Identifier (_, i)) = [i]
  
  instance Print Reg where
    prt i = \case
     RSeq reg0 reg -> prPrec i 2 (concat [prt 2 reg0 , prt 3 reg])
     RAlt reg0 reg -> prPrec i 1 (concat [prt 1 reg0 , ["|"] , prt 2 reg])
  
     -- Flex does not support set difference. See link for valid patterns.
     -- https://westes.github.io/flex/manual/Patterns.html#Patterns
     -- RMinus reg0 reg -> prPrec i 1 (concat [prt 2 reg0 , ["#"] , prt 2 reg])
     RMinus reg0 REps -> prt i reg0 -- REps is identity for set difference
     RMinus RAny (RChar c)   -> [ concat [ "[^", escapeChar c,             "]" ] ]
     RMinus RAny (RAlts str) -> [ concat [ "[^", concatMap escapeChar str, "]" ] ]
       -- FIXME: unicode inside brackets [...] is not accepted by flex
     -- FIXME: maybe we could add cases for char - RDigit, RLetter etc.
     RMinus _ _ -> error  "Flex does not support general set difference"
  
     RStar reg -> concat [ prt 3 reg , ["*"] ]
     RPlus reg -> concat [ prt 3 reg , ["+"] ]
     ROpt reg  -> concat [ prt 3 reg , ["?"] ]
     REps      -> [ flexEps ]
     RChar c   -> [ escapeChar c ]
     -- Unicode characters cannot be inside [...] so we use | instead.
     RAlts str -> prPrec i 1 $ List.intersperse "|" $ map escapeChar str
       -- RAlts str -> concat [["["], prt 0 $ concatMap escapeChar str, ["]"]]
     RSeqs str -> prPrec i 2 $ map escapeChar str
     RDigit    -> [ "{DIGIT}"   ]
     RLetter   -> [ "{LETTER}"  ]
     RUpper    -> [ "{CAPITAL}" ]
     RLower    -> [ "{SMALL}"   ]
     RAny      -> [ "."         ]
  
  -- | Handle special characters in regular expressions.
  
  escapeChar :: Char -> String
  escapeChar c
    | c `elem` reserved       = '\\':[c]
    | let x = ord c, x >= 256 = [c]  -- keep unicode characters -- "\x" ++ showHex x ""
    | otherwise               = showLitChar c ""
    where
    reserved :: String
    reserved = " $+-*=<>[](){}!?.,;:^~|&%#/\\$_@\""
  