packages feed

tup-functor-0.3.0.0: 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 = (Cons a (Cons b (Cons c Empty)))
-- > x = (NTup3 (a,b,c))
-- 
-- This should also work in pattern context.
--

{-# 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)

--------------------------------------------------------------------------------
-- vec

-- | A string constructor (used for Cons and Empty)
constructor :: String -> Exp
constructor con = Var $ UnQual $ Ident (con)

toVecListE ::  Exp -> Exp
toVecListE e = 
  case e of
    Con (Special UnitCon) -> go []
    Tuple boxed es -> go es 
    _              -> go [e]
  where
    go [] = constructor "Empty"
    go (x:xs) = App (App (constructor "Cons") x) (go xs)

--------------------------------------------------------------------------------
-- tup

-- | The constructor Tup<K>
tupleCon :: Int -> Exp
tupleCon k = Var $ UnQual $ Ident ("Tup" ++ show k)

toTupleE :: Exp -> Exp
toTupleE e = case e of
  Con (Special UnitCon) -> tupleCon 0
  Tuple boxed es -> foldl App (tupleCon (length es)) es
  _              -> App (tupleCon 1) e

--------------------------------------------------------------------------------
-- ntup

-- | The constructor NTup<K>
ntupCon :: Int -> Exp
ntupCon k = Var $ UnQual $ Ident ("NTup" ++ show k)

toNewtypeE :: Exp -> Exp
toNewtypeE e = case e of
  Con (Special UnitCon) -> ntupCon 0
  Tuple boxed es -> App (ntupCon (length es)) e
  _              -> App (ntupCon 1) e

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

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

toTupleS :: Cfg -> String -> String
toTupleS cfg s = {- trace ("|"++s++"|") $ -} case parseExp (inparens s) of
  ParseOk e -> case cfg of
    TupPP  -> pp (toTupleE   e)
    VecPP  -> pp (toVecListE e)
    NTupPP -> pp (toNewtypeE e)
  err -> error ("tuplepp: parse error in tuple 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 ("tuplepp: 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 = TupPP | VecPP | NTupPP deriving (Eq,Show)

tupCfg, vecCfg, ntupCfg :: Cfg
tupCfg  = TupPP
vecCfg  = VecPP
ntupCfg = NTupPP

tupOpts, vecOpts, newtypeOpts, possibleOpts :: [String]
tupOpts      = [ "-t" , "--tup" ] 
vecOpts      = [ "-v" , "--vec" ]
newtypeOpts  = [ "-n" , "--ntup" , "--newtype" ]
possibleOpts = vecOpts ++ tupOpts ++ newtypeOpts

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
            | not (null (intersect opts newtypeOpts))  = ntupCfg
            | 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:"
          , "  tuplepp [options] <input.hs >output.hs"
          , "  tuplepp [options] input.hs >output.hs"
          , "  tuplepp [options] input.hs output.hs"
          , "  tuplepp [options] dummy.hs input.hs output.hs     -- this is for GHC"
          , "options:"
          , "  -t, --tup : generates (Tup3 105 106 107) expressions/patterns"
          , "  -v, --vec : generates (Cons 105 (Cons 106 (Cons 107 Empty))) expressions/patterns"
          , "  -n, --ntup, --newtype : generates (NTup3 (105,106,107)) expressions/patterns"
          ]
      
  where
    f cfg src text = processLine cfg src text

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