module Main where import CommandArgs import Control.Monad (liftM, when) import Control.Applicative ( (<|>) ) import Data.List import Data.Maybe import Data.ByteString.UTF8 as BS8 import Data.ByteString.Lazy.UTF8 as LBS8 import Data.Aeson as JSON import Data.Yaml as YAML import Prelude hiding (getContents) import System.FilePath import System.IO (withFile, IOMode (ReadMode, WriteMode), hPutStr, hPutStrLn, stdout) import System.IO.Strict import Text.HPaco.Reader (Reader) import Text.HPaco.Readers.Paco import Text.HPaco.Optimizer import Text.HPaco.Writer as W import qualified Text.HPaco.Writers.PHP as PHP import qualified Text.HPaco.Writers.Run as Run import qualified Text.HPaco.Writers.Javascript as JS import qualified Text.HPaco.Writers.Dependencies as Deps import qualified Text.HPaco.Writers.JsonLisp as JsonLisp import Text.HPaco.Writer (Writer) import Data.Char main = do argStruct <- parseArgs let optimizationLevel = caOptimizationLevel argStruct when (optimizationLevel > 1) $ putStrLn $ "Warning: Optimization level " ++ show optimizationLevel ++ " requested, using 1 instead" let filenames = caInputFiles argStruct if null filenames then processStdin argStruct else mapM_ (processFile argStruct) filenames processStdin :: CommandArgs -> IO () processStdin cmdargs = getContents >>= process cmdargs "" "" >>= putStr outputFilename :: CommandArgs -> FilePath -> FilePath outputFilename cmdargs srcFilename = replaceExtension srcFilename (outputFormatExtension . caOutputFormat $ cmdargs) processFile :: CommandArgs -> FilePath -> IO () processFile cmdargs srcFilename = do src <- withFile srcFilename ReadMode hGetContents let dstFilename = outputFilename cmdargs srcFilename dst <- process cmdargs (takeBaseName srcFilename) srcFilename src if caToStdout cmdargs then putStr dst else withFile dstFilename WriteMode $ flip hPutStr dst roundTripYaml :: Monad m => String -> m String roundTripYaml s | all isSpace s = return "" | otherwise = maybe (fail "Invalid YAML") (return . LBS8.toString . JSON.encode) . (YAML.decode :: BS8.ByteString -> Maybe JSON.Value) . BS8.fromString $ s process :: CommandArgs -> String -> FilePath -> String -> IO String process cmdargs templateName filename src = do let reader = getReader cmdargs filename filename write = getWriter cmdargs templateName rundata <- (roundTripYaml $ caRunData cmdargs) let flavor = getFlavor cmdargs (tagO, tagC) = pfTagMarkers flavor pre = tagO ++ "with " ++ rundata ++ " " ++ tagC ++ "\n" post = tagO ++ "endwith" ++ tagC ++ "\n" src' = if null rundata then src else pre ++ src ++ post optimizationLevel = caOptimizationLevel cmdargs opt = case optimizationLevel of 0 -> id otherwise -> optimize ast <- reader src' case caOutputFormat cmdargs of RunInterpreted -> runInterpreted (opt ast) cmdargs templateName >> return "\n" ListDependencies -> return . getWriter cmdargs filename $ ast otherwise -> return . write . opt $ ast where runInterpreted ast cmdargs templateName = let opts = Run.defaultOptions { Run.roTemplateName = templateName `fromMaybe` caTemplateName cmdargs } in Run.run opts ast outputFormatExtension :: OutputFormat -> String outputFormatExtension OutputPHP = "php" outputFormatExtension OutputJavascript = "js" outputFormatExtension OutputJsonLisp = "jslisp" outputFormatExtension RunInterpreted = "" outputFormatExtension ListDependencies = "dep" getFlavor :: CommandArgs -> PacoFlavor getFlavor cmdargs = case caPacoFlavor cmdargs of Default -> defaultPacoFlavor Jinja -> jinjaPacoFlavor CSS -> cssPacoFlavor getReader :: CommandArgs -> FilePath -> Reader getReader cmdargs filePath = case getEffectiveInputLanguage cmdargs filePath of Paco -> readPaco' flavor Capo -> readCapo' flavor where flavor = getFlavor cmdargs getEffectiveInputLanguage :: CommandArgs -> FilePath -> InputLanguage getEffectiveInputLanguage cmdargs filePath = let Just effectiveInputLanguage = caInputLanguage cmdargs <|> inputLanguageFromFilename filePath <|> Just Paco in effectiveInputLanguage inputLanguageFromFilename :: FilePath -> Maybe InputLanguage inputLanguageFromFilename fn = case takeExtension fn of ".paco" -> Just Paco ".capo" -> Just Capo otherwise -> Nothing getWriter :: CommandArgs -> String -> Writer getWriter cmdargs templateName = go (caOutputFormat cmdargs) cmdargs templateName where go :: OutputFormat -> CommandArgs -> String -> Writer go DumpRawAST _ _ = show go ListDependencies cmdargs templateName = Deps.writeDependencies templateName go OutputJsonLisp cmdargs templateName = JsonLisp.writeJsonLisp go OutputJavascript cmdargs templateName = JS.writeJavascript $ JS.defJsWriterOptions { woPrettyPrint = caPretty cmdargs , woTemplateName = templateName `fromMaybe` caTemplateName cmdargs , woIncludePreamble = not . caNoPreamble $ cmdargs , woWrapMode = caWrapMode cmdargs , woSourcePositionComments = caSourcePositions cmdargs } go OutputPHP cmdargs templateName = PHP.writePHP $ PHP.defPHPWriterOptions { woPrettyPrint = caPretty cmdargs , woTemplateName = templateName `fromMaybe` caTemplateName cmdargs , woIncludePreamble = not . caNoPreamble $ cmdargs , woWrapMode = caWrapMode cmdargs , woExposeAllFunctions = caPhpExposeAllFunctions cmdargs , woSourcePositionComments = caSourcePositions cmdargs }