[Haskell-cafe] Compile-time here document facility

Dave Bayer bayer at cpw.math.columbia.edu
Fri Jun 22 17:21:34 EDT 2007


I couldn't find a compile-time here document facility, so I wrote one  
using Template Haskell:

> module HereDocs(hereDocs) where
>
> import Control.Exception
> import Language.Haskell.TH.Syntax
>
> getDoc :: String -> [String] -> (String,[String])
> getDoc eof txt =
>     let (doc,rest) = break (== eof) txt
>     in  (unlines doc, drop 1 rest)
>
> makeVal :: String -> String -> [Dec]
> makeVal var doc = let name = mkName var in
>     [SigD name (ConT (mkName "String")),
>     ValD (VarP name) (NormalB (LitE (StringL doc))) []]
>
> scanSrc :: [Dec] -> [String] -> Q [Dec]
> scanSrc vals [] = return vals
> scanSrc vals (x:xs) = case words x of
>     [var, "=", ('<':'<':eof)] ->
>         let (doc,rest) = getDoc eof xs
>             val = makeVal var doc
>         in  scanSrc (vals ++ val) rest
>     _ -> scanSrc vals xs
>
> hereDocs :: FilePath -> Q [Dec]
> hereDocs src =
>     let fin = catchJust assertions (evaluate src) (return.takeWhile  
> (/= ':'))
>     in  runIO (fin >>= readFile >>= return . lines) >>= scanSrc []

One binds here documents embedded in comments by writing

> import HereDocs
> $(hereDocs "Main.hs")

As an idiom, one can refer to the current file as follows; the first  
thing hereDocs does is catch assert errors in order to learn the file  
name:

> import HereDocs
> $(hereDocs $ assert False "")

Here is an example use:

> {-# OPTIONS_GHC -fth -Wall -Werror #-}
>
> module Main where
>
> import System
> import Control.Exception
>
> import HereDocs
> $(hereDocs $ assert False "")
>
> {-
> ruby = <<RUBY
> #!/usr/bin/env ruby
> hello = <<EOF
> Ruby is not
>    an acceptable Lisp
> EOF
> puts hello
> RUBY
>
> lisp = <<LISP
> #!/usr/bin/env mzscheme -qr
> (display #<<EOF
> Lisp is not
>    an acceptable Haskell
> EOF
> )
> (newline)
> LISP
> -}
>
> exec :: FilePath -> String -> IO ExitCode
> exec fout str = do
>    writeFile fout str
>    system ("chmod +x " ++ fout ++ "; ./" ++ fout)
>
> main :: IO ExitCode
> main = do
>    exec "hello.rb" ruby
>    exec "hello.scm" lisp




More information about the Haskell-Cafe mailing list