packages feed

antlrc-0.0.2: utils/antlrcmkenums/Main.hs

-- Copyright (c)2010, Mark Wright.  All rights reserved.

module Main (main) where
import Data.Enumerator
import qualified Data.ByteString as B8
import Control.Exception as E
import qualified Data.List as L
import Control.Monad (liftM, unless)
import System.IO
import System.IO.Error (isEOFError)
import System.Environment
import Text.Regex.Posix
import Data.Char

tokenLineConsComma :: (Int, B8.ByteString) -> (Int, B8.ByteString)
tokenLineConsComma (n, b) =
  (n + 1, b')
  where
    b' = if n == 0
           then indent `B8.append` b
           else c `B8.cons` (nl `B8.cons` (indent `B8.append` b))
    c = toEnum (fromEnum ',')
    nl = toEnum (fromEnum '\n')
    indent = B8.replicate 2 (toEnum (fromEnum ' '))

tokenLine :: (Int, B8.ByteString) -> (Int, B8.ByteString)
tokenLine (n, b) =
  let tl = b =~ "[^\']*" :: B8.ByteString
  in
    if B8.null tl
       then (n, B8.empty)
       else tokenLineConsComma (n, tl)

tokenLines :: Handle -> Int -> [B8.ByteString] -> IO Int
tokenLines h acc bytes =
  let (n, tl) = L.foldl' sumLines (acc, B8.empty) (zip [acc..] bytes)
      sumLines :: (Int, B8.ByteString) -> (Int, B8.ByteString) -> (Int, B8.ByteString)
      sumLines (m, s) (n, t) =
        (x, u)
        where
          (x, t') = tokenLine (n, t)
          u = s `B8.append` t'
  in do
     B8.hPutStr h tl
     return n

iterHandleLines :: Handle -> Iteratee B8.ByteString IO Int
iterHandleLines h = continue (step 0) where
  step acc EOF = yield acc EOF
  step acc (Chunks bytes) = Iteratee $ do
    eitherErr <- E.try $ tokenLines h acc bytes
    return $ case eitherErr of
      Left err -> Error err
      Right lines -> Continue (step lines)

enumHandleLines :: Handle -> Enumerator B8.ByteString IO a
enumHandleLines h = Iteratee . loop where
  loop (Continue k) = do
    eitherBytes <- E.try (B8.hGetLine h)
    case eitherBytes of
      Left err -> if isEOFError err
                     then return  (Continue k)
                     else return $ Error $ E.toException err
      Right bytes | B8.null bytes -> return (Continue k)
      Right bytes -> runIteratee (k (Chunks [bytes])) >>= loop
  loop step = return step

enumFileLines :: FilePath -> Enumerator B8.ByteString IO b
enumFileLines path s = Iteratee $ do
  eitherH <- E.try $ openFile path ReadMode
  case eitherH of
    Left err -> return $ Error err
    Right h -> finally
               (runIteratee (enumHandleLines h s))
               (hClose h)

filePaths :: [String] -> (FilePath, FilePath)
filePaths args
  | argc == 1 = (inputFilePath, outFile inputFilePath)
  | otherwise = (inputFilePath, args !! 1)
  where
    argc = L.length args
    inputFilePath = L.head args
    outFile :: String -> FilePath
    outFile s = (s =~ "[^\\.]*") ++ "Tokens.h"

header :: Handle -> FilePath -> IO ()
header h s =
  let grammarName = s =~ "[^\\.]*" :: String
      headerGuardName = toUpper (L.head grammarName) : tail grammarName ++ "Tokens_H"
      tokensEnumName = toUpper (L.head grammarName) : tail grammarName ++ "Tokens"
  in
    do
      hPutStrLn h $ "#ifndef " ++ headerGuardName
      hPutStrLn h $ "#define " ++ headerGuardName ++ "\n"
      hPutStrLn h "#include <antlr3commontoken.h>\n"
      hPutStrLn h "#undef EOF\n"
      hPutStrLn h $ "enum " ++ tokensEnumName ++ "\n{"

trailer :: Handle -> IO ()
trailer h = do
  hPutStrLn h "\n};\n"
  hPutStrLn h "#endif\n"

main :: IO Int
main = do
  args <- getArgs
  let argc = L.length args
  if (argc >= 1) && (argc <= 2)
    then do
      let (inputFilePath, outputFilePath) = filePaths args
          enum = enumFileLines inputFilePath
      eitherH <- E.try $ openFile outputFilePath WriteMode
      case eitherH of
        Left err ->
          putStrLn "Failed to open output file " >> ioError err >> return 1
        Right h ->
          finally
          (header h inputFilePath >> run_ (enum $$ iterHandleLines h) >> trailer h >> return 0)
          (hClose h)
    else do
      putStrLn "Usage: antlrcmkenums grammar.tokens [grammarTokens.h]"
      putStrLn "antlrcmkenums reads the grammar.tokens file that is generated from grammar.g by"
      putStrLn "ANTLR. antlrcmkenums outputs a C/C++ header file grammarTokens.h containing the"
      putStrLn "C/C++ definition of an enum grammarToeksn, with an enum element for each token"
      putStrLn "that has been assigned a name string."
      return 1