-- -----------------------------------------------------------------------------
-- 
-- Alex.hs, part of Alex
--
-- (c) Chris Dornan 1995-2000, Simon Marlow 2003
--
-- ----------------------------------------------------------------------------}

module Text.Alex (
    runAlex
  , CLIFlags(..)
  , alex
  , optsToInject
  , importsToInject
  
  , parseScript, Target(..)
  ) where

import AbsSyn
import CharSet
import DFA
import Info
import Map ( Map )
import qualified Map hiding ( Map )
import Output
import ParseMonad ( runP )
import Parser
import Scan

import Data.Char ( chr )



runAlex :: [CLIFlags] -> Maybe FilePath -> String -> (String,String)
runAlex cli file prg =
  let script = parseScript file prg in
  alex cli script

parseScript :: Maybe FilePath -> String
  -> (Maybe (AlexPosn,Code), [Directive], Scanner, Maybe (AlexPosn,Code))
parseScript maybeFile prg =
  let file = maybe "<no file>" id maybeFile in
  case runP prg initialParserEnv parse of
	Left (Just (AlexPn _ line col),err) -> 
		error (file ++ ":" ++ show line ++ ":" ++ show col
				 ++ ": " ++ err ++ "\n")
	Left (Nothing, err) ->
		error (file ++ ": " ++ err ++ "\n")

	Right script -> script



alex :: [CLIFlags]
     -> (Maybe (AlexPosn, Code), [Directive], Scanner, Maybe (AlexPosn, Code))
     -> (String,String)
alex cli script =
  let 
    target 
      | OptGhcTarget `elem` cli = GhcTarget
      | otherwise               = HaskellTarget
    (maybe_header, directives, scanner1, maybe_footer) = script
    (scanner2, scs, sc_hdr) = encodeStartCodes scanner1
    (scanner_final, actions) = extractActions scanner2
    dfa = scanner2dfa scanner_final scs
    nm  = scannerName scanner_final
  in
   (maybe id ((++) . snd) (maybe_header) $ 
     maybe id (flip (++) . snd) (maybe_footer) $ 
     outputDFA target 1 nm dfa "" ++ (actions "") ++ (sc_hdr "")
      ,(infoDFA 1 nm dfa ""))

optsToInject :: Target -> [CLIFlags] -> String
optsToInject GhcTarget _ = "{-# OPTIONS -fglasgow-exts -cpp #-}\n"
optsToInject _         _ = "{-# OPTIONS -cpp #-}\n"

importsToInject :: Target -> [CLIFlags] -> String
importsToInject _ cli = always_imports ++ debug_imports ++ glaexts_import
  where
	glaexts_import | OptGhcTarget `elem` cli    = import_glaexts
		       | otherwise                  = ""

	debug_imports  | OptDebugParser `elem` cli = import_debug
		       | otherwise		   = ""

-- CPP is turned on for -fglasogw-exts, so we can use conditional
-- compilation.  We need to #include "config.h" to get hold of
-- WORDS_BIGENDIAN (see GenericTemplate.hs).

always_imports :: String
always_imports = "#if __GLASGOW_HASKELL__ >= 603\n" ++
		 "#include \"ghcconfig.h\"\n" ++
		 "#elif defined(__GLASGOW_HASKELL__)\n" ++
		 "#include \"config.h\"\n" ++
		 "#endif\n" ++
		 "#if __GLASGOW_HASKELL__ >= 503\n" ++
		 "import Data.Array\n" ++
		 "import Data.Char (ord)\n" ++
		 "import Data.Array.Base (unsafeAt)\n" ++
		 "#else\n" ++
		 "import Array\n" ++
		 "import Char (ord)\n" ++
		 "#endif\n"

import_glaexts :: String
import_glaexts = "#if __GLASGOW_HASKELL__ >= 503\n" ++
		 "import GHC.Exts\n" ++
		 "#else\n" ++
		 "import GlaExts\n" ++
		 "#endif\n"

import_debug :: String 
import_debug   = "#if __GLASGOW_HASKELL__ >= 503\n" ++
		 "import System.IO\n" ++
		 "import System.IO.Unsafe\n" ++
		 "import Debug.Trace\n" ++
		 "#else\n" ++
		 "import IO\n" ++
		 "import IOExts\n" ++
		 "#endif\n"

initialParserEnv :: (Map String CharSet, Map String RExp)
initialParserEnv = (initSetEnv, initREEnv)

initSetEnv :: Map String CharSet
initSetEnv = Map.fromList [("white", charSet " \t\n\v\f\r"),
		           ("printable", charSet [chr 32 .. chr 126]),
		           (".", charSetComplement emptyCharSet 
			    `charSetMinus` charSetSingleton '\n')]

initREEnv :: Map String RExp
initREEnv = Map.empty

-- -----------------------------------------------------------------------------
-- Command-line flags

data CLIFlags 
  = OptDebugParser
  | OptGhcTarget
  | OptOutputFile FilePath
  | OptInfoFile (Maybe FilePath)
  | OptTemplateDir FilePath
  | DumpHelp
  | DumpVersion
  deriving Eq