packages feed

marxup-1.0.0: Main.hs

import Text.ParserCombinators.Parsec
-- import Text.PrettyPrint.HughesPJ hiding (char)
-- import qualified Text.PrettyPrint.HughesPJ as O


import Data.Char
import Control.Applicative hiding (many, (<|>))
import Data.List
import System.IO
import System.Environment
import Control.Monad

-- todo: parse strings inside haskell code
-- todo: output line directives.

instance Applicative (GenParser Char st) where
    (<*>) = ap
    pure = return

----------
    
type Doc = String -> String
(<>) = (.)
text s = (s ++)
x <+> y =  x <> text " " <> y 
oChar c = (c:)
parens s = oChar '(' <> s <> oChar ')'
braces s = oChar '{' <> s <> oChar '}'
brackets s = oChar '[' <> s <> oChar ']'
doubleQuotes s = oChar '"' <> s <> oChar '"'

oEmpty = id
int x = text $ show x
hcat :: [Doc] -> Doc
hcat = foldr (<>) oEmpty
punctuate t = map (<> t)
render x = x ""

------------------------------------------
-- Output combinators
    
oPos :: SourcePos -> Doc
oPos p = text "{-# LINE" <+> int (sourceLine p) <+> text (show (sourceName p)) <+> text "#-}"
    
oText :: String -> Doc
oText x = text "textual" <+> text (show x)

oMappend :: [Doc] -> Doc
oMappend [] = text "(return ())"
oMappend [x] = parens x
oMappend l = text "do" <+> braces (text "rec" <+> braces (hcat (punctuate (text ";") binds)) <> text ";" <> ret)
  where binds = init l
        ret = last l
             

oBraces :: Doc -> Doc
oBraces x = oText "{" <> text "*>" <> x <> text "<*" <>  oText "}"


----------------------------------------------
-- Parsing combinators
pChunk :: String -> Parser String
pChunk stops = many1 (noneOf stops)

pWPos :: Parser Doc 
pWPos = do 
  char '\n'
  pos <- getPosition
  return $ oPos pos <> text "\n"

pTextChunk = (oText <$> pChunk "\n{}@")

pHaskChunk = (text <$> many1 (noneOf "\n\"{}[]()@" <|> try (char '@' <* notFollowedBy (oneOf "[({\""))))

pHaskLn = pWPos -- before each newline, tell GHC where we are.

pTextLn = do
    (oText "\n" <>) <$> pWPos 
  -- add code to output a newline; and insert a newline in the code.
  

pQuote = do
  try (string "@\"")
  d <- pInText (string "@\"")
  return $ parens d

box x = [x]

pString = do
  char '"'
  result <- many (string "\\\"" <|> pChunk ['"'])
  char '"'
  return $ doubleQuotes . hcat . map text $ result

-- | Parse some Haskell code with markup inside. 
pHask = hcat <$> many ((brackets <$> pArg "[]") <|> 
                       (braces   <$> pArg "{}") <|>
                       (parens   <$> pArg "()") <|> pQuote <|> pString <|> pHaskChunk <|> pHaskLn)

-- | Parse a text argument to an element
pTextArg :: Parser Doc
pTextArg = do
  char '{' 
  result <- pInText (char '}')
  return result


pInText :: Parser a -> Parser Doc
pInText end = oMappend <$> manyTill (pTextChunk <|> pTextLn <|> pElement <|> (oBraces <$> pTextArg)) (try end)

pArg :: String -> Parser Doc
pArg [open,close] = do 
  char open
  result <- pHask
  char close
  return result

pArg1 [open,close] = do
  r <- pArg [open,close]
  return $ oChar open <> r <> oChar close

isIdentChar x = isAlphaNum x || x == '\''

-- | Either @fctName or @result<-fctName
pFctName :: Parser (Maybe Doc, Doc)
pFctName = do 
  x <- try (char '@' >> satisfy isIdentChar) -- this is to clash with @"
  xs' <- many1 (satisfy isIdentChar)
  let xs = x : xs'
  ys <- option Nothing (Just <$> (try (string "<-") >> many1 (satisfy isIdentChar)))
  return $ case ys of
    Nothing -> (Nothing, text xs)
    Just ys -> (Just (text xs), text ys)

pFct = (Nothing,) <$> parens <$> 
          (try (string "@{") *> pHask <* char '}')

pElement :: Parser Doc
pElement = do
  (result,function) <- pFctName <|> pFct
  args <- many (pArg "()" <|> pTextArg)
  let binder = maybe oEmpty (<+> text "<-") result
  return $ binder <> text "element" <+> parens (function <+> (hcat $ fmap parens args))
{-
pTop :: Parser Doc
pTop = do
  t <- pInText
  return (text "import Text" $$
          text "main =" <+> t)
-}
{-
main = do
  p <- parseFromFile pTop "Text.ths"
  case p of 
    Left err -> hPutStrLn stderr $ show err
    Right res -> putStrLn $ render res
  -}

main :: IO ()
main = do
  x : y : z : _ <- getArgs
  putStrLn x
  putStrLn y
  putStrLn z
  p <- parseFromFile pHask y
  case p of 
    Left err -> do
           hPutStrLn stderr $ show err
    Right res -> writeFile z $ 
                    render res