File: MacroPass.hs

package info (click to toggle)
cpphs 1.18.5-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 812 kB
  • ctags: 21
  • sloc: haskell: 1,707; sh: 120; makefile: 49; ansic: 11
file content (184 lines) | stat: -rwxr-xr-x 9,007 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
-----------------------------------------------------------------------------
-- |
-- Module      :  MacroPass
-- Copyright   :  2004 Malcolm Wallace
-- Licence     :  LGPL
--
-- Maintainer  :  Malcolm Wallace <Malcolm.Wallace@cs.york.ac.uk>
-- Stability   :  experimental
-- Portability :  All
--
-- Perform a cpp.second-pass, accumulating \#define's and \#undef's,
-- whilst doing symbol replacement and macro expansion.
-----------------------------------------------------------------------------

module Language.Preprocessor.Cpphs.MacroPass
  ( macroPass
  , preDefine
  , defineMacro
  , macroPassReturningSymTab
  ) where

import Language.Preprocessor.Cpphs.HashDefine (HashDefine(..), expandMacro
                                              , simplifyHashDefines)
import Language.Preprocessor.Cpphs.Tokenise   (tokenise, WordStyle(..)
                                              , parseMacroCall)
import Language.Preprocessor.Cpphs.SymTab     (SymTab, lookupST, insertST
                                              , emptyST, flattenST)
import Language.Preprocessor.Cpphs.Position   (Posn, newfile, filename, lineno)
import Language.Preprocessor.Cpphs.Options    (BoolOptions(..))
import System.IO.Unsafe (unsafeInterleaveIO)
import Control.Monad    ((=<<))
import System.Time       (getClockTime, toCalendarTime, formatCalendarTime)
import System.Locale     (defaultTimeLocale)

noPos :: Posn
noPos = newfile "preDefined"

-- | Walk through the document, replacing calls of macros with the expanded RHS.
macroPass :: [(String,String)]	-- ^ Pre-defined symbols and their values
          -> BoolOptions	-- ^ Options that alter processing style
          -> [(Posn,String)]	-- ^ The input file content
          -> IO String		-- ^ The file after processing
macroPass syms options =
    fmap (safetail		-- to remove extra "\n" inserted below
         . concat
         . onlyRights)
    . macroProcess (pragma options) (layout options) (lang options)
                   (preDefine options syms)
    . tokenise (stripEol options) (stripC89 options)
               (ansi options) (lang options)
    . ((noPos,""):)	-- ensure recognition of "\n#" at start of file
  where
    safetail [] = []
    safetail (_:xs) = xs

-- | auxiliary
onlyRights :: [Either a b] -> [b]
onlyRights = concatMap (\x->case x of Right t-> [t]; Left _-> [];)

-- | Walk through the document, replacing calls of macros with the expanded RHS.
--   Additionally returns the active symbol table after processing.
macroPassReturningSymTab
          :: [(String,String)]	-- ^ Pre-defined symbols and their values
          -> BoolOptions	-- ^ Options that alter processing style
          -> [(Posn,String)]	-- ^ The input file content
          -> IO (String,[(String,String)])
				-- ^ The file and symbol table after processing
macroPassReturningSymTab syms options =
    fmap (mapFst (safetail		-- to remove extra "\n" inserted below
                 . concat)
         . walk)
    . macroProcess (pragma options) (layout options) (lang options)
                   (preDefine options syms)
    . tokenise (stripEol options) (stripC89 options)
               (ansi options) (lang options)
    . ((noPos,""):)	-- ensure recognition of "\n#" at start of file
  where
    safetail [] = []
    safetail (_:xs) = xs
    walk (Right x: rest) = let (xs,   foo) = walk rest
                           in  (x:xs, foo)
    walk (Left  x: [])   =     ( [] , simplifyHashDefines (flattenST x) )
    walk (Left  x: rest) = walk rest
    mapFst f (a,b) = (f a, b)


-- | Turn command-line definitions (from @-D@) into 'HashDefine's.
preDefine :: BoolOptions -> [(String,String)] -> SymTab HashDefine
preDefine options defines =
    foldr (insertST . defineMacro options . (\ (s,d)-> s++" "++d))
          emptyST defines

-- | Turn a string representing a macro definition into a 'HashDefine'.
defineMacro :: BoolOptions -> String -> (String,HashDefine)
defineMacro opts s =
    let (Cmd (Just hd):_) = tokenise True True (ansi opts) (lang opts)
                                     [(noPos,"\n#define "++s++"\n")]
    in (name hd, hd)


-- | Trundle through the document, one word at a time, using the WordStyle
--   classification introduced by 'tokenise' to decide whether to expand a
--   word or macro.  Encountering a \#define or \#undef causes that symbol to
--   be overwritten in the symbol table.  Any other remaining cpp directives
--   are discarded and replaced with blanks, except for \#line markers.
--   All valid identifiers are checked for the presence of a definition
--   of that name in the symbol table, and if so, expanded appropriately.
--   (Bool arguments are: keep pragmas?  retain layout?  haskell language?)
--   The result lazily intersperses output text with symbol tables.  Lines
--   are emitted as they are encountered.  A symbol table is emitted after
--   each change to the defined symbols, and always at the end of processing.
macroProcess :: Bool -> Bool -> Bool -> SymTab HashDefine -> [WordStyle]
             -> IO [Either (SymTab HashDefine) String]
macroProcess _ _ _ st        []          = return [Left st]
macroProcess p y l st (Other x: ws)      = emit x    $ macroProcess p y l st ws
macroProcess p y l st (Cmd Nothing: ws)  = emit "\n" $ macroProcess p y l st ws
macroProcess p y l st (Cmd (Just (LineDrop x)): ws)
                                         = emit "\n" $
                                           emit x    $ macroProcess p y l st ws
macroProcess pragma y l st (Cmd (Just (Pragma x)): ws)
               | pragma    = emit "\n" $ emit x $ macroProcess pragma y l st ws
               | otherwise = emit "\n" $          macroProcess pragma y l st ws
macroProcess p layout lang st (Cmd (Just hd): ws) =
    let n = 1 + linebreaks hd
        newST = insertST (name hd, hd) st
    in
    emit (replicate n '\n') $
    emitSymTab newST $
    macroProcess p layout lang newST ws
macroProcess pr layout lang st (Ident p x: ws) =
    case x of
      "__FILE__" -> emit (show (filename p))$ macroProcess pr layout lang st ws
      "__LINE__" -> emit (show (lineno p))  $ macroProcess pr layout lang st ws
      "__DATE__" -> do w <- return .
                            formatCalendarTime defaultTimeLocale "\"%d %b %Y\""
                            =<< toCalendarTime =<< getClockTime
                       emit w $ macroProcess pr layout lang st ws
      "__TIME__" -> do w <- return .
                            formatCalendarTime defaultTimeLocale "\"%H:%M:%S\""
                            =<< toCalendarTime =<< getClockTime
                       emit w $ macroProcess pr layout lang st ws
      _ ->
        case lookupST x st of
            Nothing -> emit x $ macroProcess pr layout lang st ws
            Just hd ->
                case hd of
                    AntiDefined {name=n} -> emit n $
                                            macroProcess pr layout lang st ws
                    SymbolReplacement {replacement=r} ->
                        let r' = if layout then r else filter (/='\n') r in
                        -- one-level expansion only:
                        -- emit r' $ macroProcess layout st ws
                        -- multi-level expansion:
                        macroProcess pr layout lang st
                                     (tokenise True True False lang [(p,r')]
                                      ++ ws)
                    MacroExpansion {} ->
                        case parseMacroCall p ws of
                            Nothing -> emit x $
                                       macroProcess pr layout lang st ws
                            Just (args,ws') ->
                                if length args /= length (arguments hd) then
                                     emit x $ macroProcess pr layout lang st ws
                                else do args' <- mapM (fmap (concat.onlyRights)
                                                       . macroProcess pr layout
                                                                        lang st)
                                                      args
                                        -- one-level expansion only:
                                        -- emit (expandMacro hd args' layout) $
                                        --         macroProcess layout st ws'
                                        -- multi-level expansion:
                                        macroProcess pr layout lang st
                                            (tokenise True True False lang
                                               [(p,expandMacro hd args' layout)]
                                            ++ ws')

-- | Useful helper function.
emit :: a -> IO [Either b a] -> IO [Either b a]
emit x io = do xs <- unsafeInterleaveIO io
               return (Right x:xs)
-- | Useful helper function.
emitSymTab :: b -> IO [Either b a] -> IO [Either b a]
emitSymTab x io = do xs <- unsafeInterleaveIO io
                     return (Left x:xs)