packages feed

tup-functor-0.2: preprocessor/tpp.hs

-- | Tuple brackets preprocessor.
-- Use like this:
-- 
-- > {-# OPTIONS -F -pgmF tuplepp -optF --tup #-}
-- > {-# OPTIONS -F -pgmF tuplepp -optF --vec #-}
--
-- Write code like this:
--
-- > x = {{a,b,c}}
--
-- and it will be translated to one of these:
--
-- > x = Tup3 a b c 
-- > x = vec3 a b c 
-- 
{-# LANGUAGE PackageImports #-}
module Main where

--------------------------------------------------------------------------------

import Data.Char
import Data.List (intersect)

import Language.Haskell.Exts
import "parsec2" Text.ParserCombinators.Parsec

import System.Environment

-- import Debug.Trace
-- debug x = trace (show x)

--------------------------------------------------------------------------------

tupleCon :: Cfg -> Int -> Exp
tupleCon (Cfg con) k = Var $ UnQual $ Ident (con {- "Tup"-} ++ show k)

toTupleE :: Cfg -> Exp -> Exp
toTupleE cfg e = case e of
  Tuple es -> foldl App (tupleCon cfg (length es)) es
  _        -> App (tupleCon cfg 1) e

inparens :: String -> String
inparens s = "(" ++ s ++ ")"

toTupleS :: Cfg -> String -> String
toTupleS cfg s = {- trace ("|"++s++"|") $ -} case parseExp (inparens s) of
  ParseOk e -> pp (toTupleE cfg e)
  err       -> error ("parse error in idiom bracket:\n" ++ show err)

pp :: Pretty a => a -> String
pp = prettyPrintStyleMode style mode where
  mode = defaultMode
    { layout = PPNoLayout
    }
  style = Style 
    { mode = LeftMode -- OneLineMode
    , lineLength = 1000
    , ribbonsPerLine = 1.5
    }
  
--------------------------------------------------------------------------------

data TupleLine 
  = S String
  | I TupleLine (Bool,TupleLine) TupleLine      -- pre (isTuple,bracket) post
  deriving Show

{-
xinit :: TupleLine -> TupleLine
xinit (S str) = S (init str)
xinit (I pre mid post) = I pre mid (xinit post)

xtail :: TupleLine -> TupleLine
xtail (S str) = S (tail str)
xtail (I pre mid post) = I (xtail pre) mid post
-}

lineP :: Parser TupleLine
lineP = do
  pre <- many (noneOf "{}")  
  m <- optionMaybe $ do

    char '{'
    c <- lookAhead anyChar
    mid <- case c of
      '{' -> char '{' >> lineP
      _   -> lineP

    char '}'
    d <- lookAhead anyChar
    post <- case d of
      '}' -> char '}' >> lineP
      _   -> lineP

    let b = (c=='{') && (d=='}') -- True -- if isAlphaNum c || elem c "({[_" then True else False

    return (b,mid,post)
  return $ case m of
    Nothing -> S pre
    Just (b,mid,post) -> case b of
      True  -> I (S pre) (b,mid) (post)
      False -> I (S pre) (b,mid) (post)

parseLine :: SourceName -> String -> TupleLine
parseLine src s = case runParser lineP () "" s of
  Right xx -> xx
  Left err -> error ("preprocessor parse error in file \"" ++ src ++ "\":\n" ++ show err)

convertTupleLine :: Cfg -> TupleLine -> String
convertTupleLine cfg (S ln) = ln
convertTupleLine cfg (I pre (b,mid) post) = pre' ++ mid'' ++ post' where
  pre'  = convertTupleLine cfg pre
  mid'  = convertTupleLine cfg mid
  post' = convertTupleLine cfg post
  mid''  = if b 
    then '(' : toTupleS cfg mid' ++ ")"
    else "{" ++ mid' ++ "}"
    
processLine :: Cfg -> SourceName -> String -> String    
processLine cfg src = convertTupleLine cfg . parseLine src
    
--------------------------------------------------------------------------------  

data Cfg = Cfg { _constructor :: String }

tupCfg = Cfg "Tup"
vecCfg = Cfg "vec"

vecOpts = [ "-v" , "--vec" ]
tupOpts = [ "-t" , "--tup" ] 
possibleOpts = vecOpts ++ tupOpts

main :: IO ()
main = 
  do
    args0 <- getArgs
    let opts = filter (\x ->      x `elem` possibleOpts ) args0
        args = filter (\x -> not (x `elem` possibleOpts)) args0
    let vecMode = not (null opts)
    let cfg | null opts                            = tupCfg    -- default is tup
            | not (null (intersect opts tupOpts))  = tupCfg
            | not (null (intersect opts vecOpts))  = vecCfg
            | True                                 = error "tuplepp: fatal error while parsing the options"

    case args of

      [] -> interact (f cfg "stdin")
      [inp] -> do
        text <- readFile inp
        putStrLn $ f cfg inp text
      [inp,out] -> do
        text <- readFile inp
        writeFile out $ f cfg inp text
      [inp,cpp,out] -> do             -- ghc calls the preprocessor this way
        text <- readFile cpp
        let pptext = f cfg cpp text
        writeFile out pptext

      _ -> do
        print args
        putStrLn $ unlines
          [ "usage:"
          , "  tpp [options] <input.hs >output.hs"
          , "  tpp [options] input.hs >output.hs"
          , "  tpp [options] input.hs output.hs"
          , "  tpp [options] dummy.hs input.hs output.hs     -- this is for GHC"
          , "options:"
          , "  -t, --tup : generates (Tup3 105 106 106) expressions"
          , "  -v, --vec : generates (vec3 105 106 106) expressions"
          ]
      
  where
    f cfg src text = processLine cfg src text

--------------------------------------------------------------------------------