hpp
A Haskell pre-processor
https://github.com/acowley/hpp
LTS Haskell 23.0: | 0.6.5 |
Stackage Nightly 2024-12-09: | 0.6.5 |
Latest on Hackage: | 0.6.5 |
hpp-0.6.5@sha256:a8f4386e964697557bca3240254b4f69b48b0899c79d3f9aa948e05f4f166b3b,1935
Module documentation for 0.6.5
The hpp
Executable
hpp
is a Haskell pre-processor that is also a C90-compatible
pre-processor (with the addition of a --cpp
flag). It is
packaged as both a library and an executable.
To use as a Haskell preprocessor for resolving #ifdef
conditionals
and macro expansion, an invocation might look like,
hpp -DDEBUG Foo.hs
To use as a C preprocessor, an invocation might look like,
hpp -DDEBUG --cpp foo.c
To have GHC use hpp
as the C pre-processor, add this line to the top
of a Haskell source file that makes use of the CPP
LANGUAGE
pragma,
{-# OPTIONS_GHC -cpp -pgmPhpp #-}
Or add this line to your .cabal
file:
ghc-options: -pgmPhpp
Note that you will need to ensure that the hpp
executable is available in your build environment (e.g. you can add hpp
as a build-depends
in your .cabal
file).
The hpp
Library
The hpp
executable is a command-line interface to the hpp
library. While the hpp
package has been designed to have minimal dependencies beyond what the GHC
compiler itself uses, it does include a few small, framework-free unit tests that demonstrate basic usage as a library. In the testIf
example, we preprocess the sourceIfdef
input with a starting definition equivalent to #define FOO 1
. In testArith1
, we exercise basic integer arithmetic and comparison. The hppHelper
function shows how to run your source input through the preprocessor: expand initialState (preproces mySource)
. If you want to support #include
ing other files, you will use runHpp
or streamHpp
that operate in IO
and support searching among a list of include paths.
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Trans.Except
import Data.ByteString.Char8 (ByteString)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Hpp
import System.Exit
sourceIfdef :: [ByteString]
sourceIfdef = [ "#ifdef FOO"
, "x = 42"
, "#else"
, "x = 99"
, "#endif" ]
sourceArith1 :: ByteString -> [ByteString]
sourceArith1 s = [ "#define x 3"
, "#if 5 + x > " <> s
, "yay"
, "#else"
, "boo"
, "#endif" ]
hppHelper :: HppState -> [ByteString] -> [ByteString] -> IO Bool
hppHelper st src expected =
case runExcept (expand st (preprocess src)) of
Left e -> putStrLn ("Error running hpp: " ++ show e) >> return False
Right (res, _) -> if hppOutput res == expected
then return True
else do putStr ("Expected "++show expected++", got")
print (hppOutput res)
return False
testElse :: IO Bool
testElse = hppHelper emptyHppState sourceIfdef ["x = 99\n","\n"]
testIf :: IO Bool
testIf = hppHelper (fromMaybe (error "Preprocessor definition did not parse")
(addDefinition "FOO" "1" emptyHppState))
sourceIfdef
["x = 42\n","\n"]
testArith1 :: IO Bool
testArith1 = (&&) <$> hppHelper emptyHppState (sourceArith1 "7") ["yay\n","\n"]
<*> hppHelper emptyHppState (sourceArith1 "8") ["boo\n","\n"]
main :: IO ()
main = do results <- sequenceA [testElse, testIf, testArith1]
if and results then exitWith ExitSuccess else exitWith (ExitFailure 1)
Changes
0.6.5
Bump bounds on base
and semigroups
.
0.6.4
GHC-9.0 compatibility
0.6.1
-
Added the
--only-macros
command line flag. Does not splice lines, remove comments, or do trigraph replacement. It does macro processing and #line marker output (which can be disabled with the-P
option) -
Changed the default configuration to emit
#line
markers. Can be disabled with-P
.
0.6.0
- Various bug fixes by @rahulmutt. These may change behavior not captured by the MCPP test suite.
- Switch to
unordered-containers
frombytestring-trie
for stackage compatibility - Internal refactoring
0.5.1
Added the expand
API for pure macro processing (i.e. #include
s are ignored).
0.5.0
- Redesigned library API
The
Hpp
module exports the main pieces.Hpp.Env
,Hpp.Types
, andHpp.Config
may be used for configuring the preprocessor.
0.4.0
- Simplify the parsing machinery
- Don’t remove C++-style single-line comments
- Don’t error on unknown cpp directives Previously, a line beginning with “#-}” would cause an error
- Don’t do trigraph replacement by default.
Haskell allows “??” in operator names and you can be sure
lens
uses it!
0.3.1
Address a change wherein GHC 8 will pass -include
arguments without a space between “-include” and the file to be included.
0.3
Switch to a stream processing model.
This library is designed to have minimal dependencies, so we now have a bespoke implementation of a cross between the pipes and machines libraries included.
This change was done to make some parsing operations easier, believe it or not. For example, most pre-processing is done on a line-by-line basis, but we must also support macro function applications that cross line boundaries. Thus the expansion logic can not merely be given one line at a time from an input file. Previously, a heuristic tried to combine consecutive lines before the parsing stage. Now, the parser itself is able to pull tokens in across lines when necessary.
TL;DR: The upshot is that processing /usr/include/stdio.h
on OS X (a
surprisingly complicated file!) now uses 78% of the time and 0.38%
the memory of previous versions of hpp
.
0.1
First release!