-----------------------------------------------------------------------------
-- |
-- Module      :  CppIfdef
-- Copyright   :  1999-2004 Malcolm Wallace
-- Licence     :  LGPL
-- 
-- Maintainer  :  Malcolm Wallace <Malcolm.Wallace@cs.york.ac.uk>
-- Stability   :  experimental
-- Portability :  All

-- Perform a cpp.first-pass, gathering \#define's and evaluating \#ifdef's.
-- and \#include's.
-----------------------------------------------------------------------------

module Language.Preprocessor.Cpphs.CppIfdef
  ( cppIfdef    -- :: FilePath -> [(String,String)] -> [String] -> Options
                --      -> String -> IO [(Posn,String)]
  ) where


import Text.Parse
import Language.Preprocessor.Cpphs.SymTab
import Language.Preprocessor.Cpphs.Position  (Posn,newfile,newline,newlines
                                             ,cppline,cpp2hask,newpos)
import Language.Preprocessor.Cpphs.ReadFirst (readFirst)
import Language.Preprocessor.Cpphs.Tokenise  (linesCpp,reslash)
import Language.Preprocessor.Cpphs.Options   (BoolOptions(..))
import Language.Preprocessor.Cpphs.HashDefine(HashDefine(..),parseHashDefine
                                             ,expandMacro)
import Language.Preprocessor.Cpphs.MacroPass (preDefine,defineMacro)
import Data.Char        (isDigit,isSpace,isAlphaNum)
import Data.List        (intercalate,isPrefixOf)
import Numeric          (readHex,readOct,readDec)
import System.IO.Unsafe (unsafeInterleaveIO)
import System.IO        (hPutStrLn,stderr)
import Control.Monad    (when)

-- | Run a first pass of cpp, evaluating \#ifdef's and processing \#include's,
--   whilst taking account of \#define's and \#undef's as we encounter them.
cppIfdef :: FilePath            -- ^ File for error reports
        -> [(String,String)]    -- ^ Pre-defined symbols and their values
        -> [String]             -- ^ Search path for \#includes
        -> BoolOptions          -- ^ Options controlling output style
        -> String               -- ^ The input file content
        -> IO [(Posn,String)]   -- ^ The file after processing (in lines)
cppIfdef :: FilePath
-> [(FilePath, FilePath)]
-> [FilePath]
-> BoolOptions
-> FilePath
-> IO [(Posn, FilePath)]
cppIfdef FilePath
fp [(FilePath, FilePath)]
syms [FilePath]
search BoolOptions
options =
    Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp Posn
posn SymTab HashDefine
defs [FilePath]
search BoolOptions
options ([Posn] -> KeepState
Keep []) ([FilePath] -> IO [(Posn, FilePath)])
-> (FilePath -> [FilePath]) -> FilePath -> IO [(Posn, FilePath)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [FilePath] -> [FilePath]
initial ([FilePath] -> [FilePath])
-> (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
linesCpp
  where
    posn :: Posn
posn = FilePath -> Posn
newfile FilePath
fp
    defs :: SymTab HashDefine
defs = BoolOptions -> [(FilePath, FilePath)] -> SymTab HashDefine
preDefine BoolOptions
options [(FilePath, FilePath)]
syms
    initial :: [FilePath] -> [FilePath]
initial = if BoolOptions -> Bool
literate BoolOptions
options then [FilePath] -> [FilePath]
forall a. a -> a
id else (Posn -> FilePath
cppline Posn
posnFilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
:)
-- Previous versions had a very simple symbol table  mapping strings
-- to strings.  Now the #ifdef pass uses a more elaborate table, in
-- particular to deal with parameterised macros in conditionals.


-- | Internal state for whether lines are being kept or dropped.
--   In @Drop n b ps@, @n@ is the depth of nesting, @b@ is whether
--   we have already succeeded in keeping some lines in a chain of
--   @elif@'s, and @ps@ is the stack of positions of open @#if@ contexts,
--   used for error messages in case EOF is reached too soon.
data KeepState = Keep [Posn] | Drop Int Bool [Posn]

-- | Return just the list of lines that the real cpp would decide to keep.
cpp :: Posn -> SymTab HashDefine -> [String] -> BoolOptions -> KeepState
       -> [String] -> IO [(Posn,String)]

cpp :: Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp Posn
_ SymTab HashDefine
_ [FilePath]
_ BoolOptions
_ (Keep [Posn]
ps) [] | Bool -> Bool
not ([Posn] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Posn]
ps) = do
    Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath
"Unmatched #if: positions of open context are:\n"FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++
                       [FilePath] -> FilePath
unlines ((Posn -> FilePath) -> [Posn] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map Posn -> FilePath
forall a. Show a => a -> FilePath
show [Posn]
ps)
    [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall (m :: * -> *) a. Monad m => a -> m a
return []
cpp Posn
_ SymTab HashDefine
_ [FilePath]
_ BoolOptions
_ KeepState
_ [] = [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall (m :: * -> *) a. Monad m => a -> m a
return []

cpp Posn
p SymTab HashDefine
syms [FilePath]
path BoolOptions
options (Keep [Posn]
ps) (l :: FilePath
l@(Char
'#':FilePath
x):[FilePath]
xs) =
    let ws :: [FilePath]
ws = FilePath -> [FilePath]
words FilePath
x
        cmd :: FilePath
cmd = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
ws then FilePath
"" else [FilePath] -> FilePath
forall a. [a] -> a
head [FilePath]
ws
        line :: [FilePath]
line = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
ws then [] else [FilePath] -> [FilePath]
forall a. [a] -> [a]
tail [FilePath]
ws
        sym :: FilePath
sym  = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
line then FilePath
"" else [FilePath] -> FilePath
forall a. [a] -> a
head [FilePath]
line
        rest :: [FilePath]
rest = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
line then [] else [FilePath] -> [FilePath]
forall a. [a] -> [a]
tail [FilePath]
line
        def :: (FilePath, HashDefine)
def = BoolOptions -> FilePath -> (FilePath, HashDefine)
defineMacro BoolOptions
options (FilePath
symFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
" "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> (FilePath -> FilePath) -> Maybe FilePath -> FilePath
forall b a. b -> (a -> b) -> Maybe a -> b
maybe FilePath
"1" FilePath -> FilePath
forall a. a -> a
id ([FilePath] -> Maybe FilePath
un [FilePath]
rest))
        un :: [FilePath] -> Maybe FilePath
un [FilePath]
v = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
v then Maybe FilePath
forall a. Maybe a
Nothing else FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just ([FilePath] -> FilePath
unwords [FilePath]
v)
        keepIf :: Bool -> KeepState
keepIf Bool
b = if Bool
b then [Posn] -> KeepState
Keep (Posn
pPosn -> [Posn] -> [Posn]
forall a. a -> [a] -> [a]
:[Posn]
ps) else Int -> Bool -> [Posn] -> KeepState
Drop Int
1 Bool
False (Posn
pPosn -> [Posn] -> [Posn]
forall a. a -> [a] -> [a]
:[Posn]
ps)
        skipn :: SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms' Bool
retain KeepState
ud [FilePath]
xs' =
            let n :: Int
n = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ FilePath -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'\n') FilePath
l) in
            (if BoolOptions -> Bool
macros BoolOptions
options Bool -> Bool -> Bool
&& Bool
retain then (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne  (Posn
p,FilePath -> FilePath
reslash FilePath
l)
                                         else [(Posn, FilePath)]
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. [a] -> IO [a] -> IO [a]
emitMany (Int -> (Posn, FilePath) -> [(Posn, FilePath)]
forall a. Int -> a -> [a]
replicate Int
n (Posn
p,FilePath
""))) (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
            Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp (Int -> Posn -> Posn
newlines Int
n Posn
p) SymTab HashDefine
syms' [FilePath]
path BoolOptions
options KeepState
ud [FilePath]
xs'
    in case FilePath
cmd of
        FilePath
"define" -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn ((FilePath, HashDefine) -> SymTab HashDefine -> SymTab HashDefine
forall v. (FilePath, v) -> SymTab v -> SymTab v
insertST (FilePath, HashDefine)
def SymTab HashDefine
syms) Bool
True ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
        FilePath
"undef"  -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn (FilePath -> SymTab HashDefine -> SymTab HashDefine
forall v. FilePath -> SymTab v -> SymTab v
deleteST FilePath
sym SymTab HashDefine
syms) Bool
True ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
        FilePath
"ifndef" -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False (Bool -> KeepState
keepIf (Bool -> Bool
not (FilePath -> SymTab HashDefine -> Bool
forall v. FilePath -> SymTab v -> Bool
definedST FilePath
sym SymTab HashDefine
syms))) [FilePath]
xs
        FilePath
"ifdef"  -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False (Bool -> KeepState
keepIf      (FilePath -> SymTab HashDefine -> Bool
forall v. FilePath -> SymTab v -> Bool
definedST FilePath
sym SymTab HashDefine
syms)) [FilePath]
xs
        FilePath
"if"     -> do Bool
b <- Posn -> SymTab HashDefine -> FilePath -> IO Bool
gatherDefined Posn
p SymTab HashDefine
syms ([FilePath] -> FilePath
unwords [FilePath]
line)
                       SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False (Bool -> KeepState
keepIf Bool
b) [FilePath]
xs
        FilePath
"else"   -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False (Int -> Bool -> [Posn] -> KeepState
Drop Int
1 Bool
False [Posn]
ps) [FilePath]
xs
        FilePath
"elif"   -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False (Int -> Bool -> [Posn] -> KeepState
Drop Int
1 Bool
True [Posn]
ps) [FilePath]
xs
        FilePath
"endif"  | [Posn] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Posn]
ps ->
                    do Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath
"Unmatched #endif at "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p
                       [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall (m :: * -> *) a. Monad m => a -> m a
return []
        FilePath
"endif"  -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False ([Posn] -> KeepState
Keep ([Posn] -> [Posn]
forall a. [a] -> [a]
tail [Posn]
ps)) [FilePath]
xs
        FilePath
"pragma" -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
True  ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
        (Char
'!':FilePath
_)  -> SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs       -- \#!runhs scripts
        FilePath
"include"-> do (FilePath
inc,FilePath
content) <- FilePath -> Posn -> [FilePath] -> Bool -> IO (FilePath, FilePath)
readFirst (SymTab HashDefine -> FilePath -> FilePath
file SymTab HashDefine
syms ([FilePath] -> FilePath
unwords [FilePath]
line))
                                                  Posn
p [FilePath]
path
                                                  (BoolOptions -> Bool
warnings BoolOptions
options)
                       Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp Posn
p SymTab HashDefine
syms [FilePath]
path BoolOptions
options ([Posn] -> KeepState
Keep [Posn]
ps)
                             ((FilePath
"#line 1 "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath -> FilePath
forall a. Show a => a -> FilePath
show FilePath
inc)FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: FilePath -> [FilePath]
linesCpp FilePath
content
                                                    [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ Posn -> FilePath
cppline (Posn -> Posn
newline Posn
p)FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath]
xs)
        FilePath
"warning"-> if BoolOptions -> Bool
warnings BoolOptions
options then
                      do Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr (FilePath
lFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
"\nin "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p)
                         SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
                    else SymTab HashDefine
-> Bool -> KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn SymTab HashDefine
syms Bool
False ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
        FilePath
"error"  -> FilePath -> IO [(Posn, FilePath)]
forall a. HasCallStack => FilePath -> a
error (FilePath
lFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
"\nin "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p)
        FilePath
"line"   | (Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit FilePath
sym
                 -> (if BoolOptions -> Bool
locations BoolOptions
options Bool -> Bool -> Bool
&& BoolOptions -> Bool
hashline BoolOptions
options then (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath
l)
                     else if BoolOptions -> Bool
locations BoolOptions
options then (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath -> FilePath
cpp2hask FilePath
l)
                     else IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> a
id) (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
                    Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp (Int -> Maybe FilePath -> Posn -> Posn
newpos (FilePath -> Int
forall a. Read a => FilePath -> a
read FilePath
sym) ([FilePath] -> Maybe FilePath
un [FilePath]
rest) Posn
p)
                        SymTab HashDefine
syms [FilePath]
path BoolOptions
options ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
        FilePath
n | (Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit FilePath
n Bool -> Bool -> Bool
&& Bool -> Bool
not (FilePath -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
n)
                 -> (if BoolOptions -> Bool
locations BoolOptions
options Bool -> Bool -> Bool
&& BoolOptions -> Bool
hashline BoolOptions
options then (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath
l)
                     else if BoolOptions -> Bool
locations BoolOptions
options then (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath -> FilePath
cpp2hask FilePath
l)
                     else IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> a
id) (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
                    Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp (Int -> Maybe FilePath -> Posn -> Posn
newpos (FilePath -> Int
forall a. Read a => FilePath -> a
read FilePath
n) ([FilePath] -> Maybe FilePath
un ([FilePath] -> [FilePath]
forall a. [a] -> [a]
tail [FilePath]
ws)) Posn
p)
                        SymTab HashDefine
syms [FilePath]
path BoolOptions
options ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
          | Bool
otherwise
                 -> do Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (BoolOptions -> Bool
warnings BoolOptions
options) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
                           Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr (FilePath
"Warning: unknown directive #"FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
n
                                             FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
"\nin "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p)
                       (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath
l) (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
                               Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp (Posn -> Posn
newline Posn
p) SymTab HashDefine
syms [FilePath]
path BoolOptions
options ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs

cpp Posn
p SymTab HashDefine
syms [FilePath]
path BoolOptions
options (Drop Int
n Bool
b [Posn]
ps) ((Char
'#':FilePath
x):[FilePath]
xs) =
    let ws :: [FilePath]
ws = FilePath -> [FilePath]
words FilePath
x
        cmd :: FilePath
cmd = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
ws then FilePath
"" else [FilePath] -> FilePath
forall a. [a] -> a
head [FilePath]
ws
        delse :: KeepState
delse    | Int
nInt -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==Int
1 Bool -> Bool -> Bool
&& Bool
b = Int -> Bool -> [Posn] -> KeepState
Drop Int
1 Bool
b [Posn]
ps
                 | Int
nInt -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==Int
1      = [Posn] -> KeepState
Keep [Posn]
ps
                 | Bool
otherwise = Int -> Bool -> [Posn] -> KeepState
Drop Int
n Bool
b [Posn]
ps
        dend :: KeepState
dend     | Int
nInt -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==Int
1      = [Posn] -> KeepState
Keep ([Posn] -> [Posn]
forall a. [a] -> [a]
tail [Posn]
ps)
                 | Bool
otherwise = Int -> Bool -> [Posn] -> KeepState
Drop (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Bool
b ([Posn] -> [Posn]
forall a. [a] -> [a]
tail [Posn]
ps)
        delif :: Bool -> KeepState
delif Bool
v  | Int
nInt -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==Int
1 Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
b Bool -> Bool -> Bool
&& Bool
v
                             = [Posn] -> KeepState
Keep [Posn]
ps
                 | Bool
otherwise = Int -> Bool -> [Posn] -> KeepState
Drop Int
n Bool
b [Posn]
ps
        skipn :: KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn KeepState
ud [FilePath]
xs' =
                 let n' :: Int
n' = Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ FilePath -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'\n') FilePath
x) in
                 [(Posn, FilePath)]
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. [a] -> IO [a] -> IO [a]
emitMany (Int -> (Posn, FilePath) -> [(Posn, FilePath)]
forall a. Int -> a -> [a]
replicate Int
n' (Posn
p,FilePath
"")) (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
                    Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp (Int -> Posn -> Posn
newlines Int
n' Posn
p) SymTab HashDefine
syms [FilePath]
path BoolOptions
options KeepState
ud [FilePath]
xs'
    in
    if      FilePath
cmd FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"ifndef" Bool -> Bool -> Bool
||
            FilePath
cmd FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"if"     Bool -> Bool -> Bool
||
            FilePath
cmd FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"ifdef" then    KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn (Int -> Bool -> [Posn] -> KeepState
Drop (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Bool
b (Posn
pPosn -> [Posn] -> [Posn]
forall a. a -> [a] -> [a]
:[Posn]
ps)) [FilePath]
xs
    else if FilePath
cmd FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"elif"  then do Bool
v <- Posn -> SymTab HashDefine -> FilePath -> IO Bool
gatherDefined Posn
p SymTab HashDefine
syms ([FilePath] -> FilePath
unwords ([FilePath] -> [FilePath]
forall a. [a] -> [a]
tail [FilePath]
ws))
                                   KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn (Bool -> KeepState
delif Bool
v) [FilePath]
xs
    else if FilePath
cmd FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"else"  then    KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn  KeepState
delse [FilePath]
xs
    else if FilePath
cmd FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"endif" then
            if [Posn] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Posn]
ps then do Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$ FilePath
"Unmatched #endif at "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p
                               [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall (m :: * -> *) a. Monad m => a -> m a
return []
                       else KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn  KeepState
dend  [FilePath]
xs
    else KeepState -> [FilePath] -> IO [(Posn, FilePath)]
skipn (Int -> Bool -> [Posn] -> KeepState
Drop Int
n Bool
b [Posn]
ps) [FilePath]
xs
        -- define, undef, include, error, warning, pragma, line

cpp Posn
p SymTab HashDefine
syms [FilePath]
path BoolOptions
options (Keep [Posn]
ps) (FilePath
x:[FilePath]
xs) =
    let p' :: Posn
p' = Posn -> Posn
newline Posn
p in Posn -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
seq Posn
p' (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
    (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath
x)  (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$  Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp Posn
p' SymTab HashDefine
syms [FilePath]
path BoolOptions
options ([Posn] -> KeepState
Keep [Posn]
ps) [FilePath]
xs
cpp Posn
p SymTab HashDefine
syms [FilePath]
path BoolOptions
options d :: KeepState
d@(Drop Int
_ Bool
_ [Posn]
_) (FilePath
_:[FilePath]
xs) =
    let p' :: Posn
p' = Posn -> Posn
newline Posn
p in Posn -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
seq Posn
p' (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$
    (Posn, FilePath) -> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a. a -> IO [a] -> IO [a]
emitOne (Posn
p,FilePath
"") (IO [(Posn, FilePath)] -> IO [(Posn, FilePath)])
-> IO [(Posn, FilePath)] -> IO [(Posn, FilePath)]
forall a b. (a -> b) -> a -> b
$  Posn
-> SymTab HashDefine
-> [FilePath]
-> BoolOptions
-> KeepState
-> [FilePath]
-> IO [(Posn, FilePath)]
cpp Posn
p' SymTab HashDefine
syms [FilePath]
path BoolOptions
options KeepState
d [FilePath]
xs


-- | Auxiliary IO functions
emitOne  ::  a  -> IO [a] -> IO [a]
emitMany :: [a] -> IO [a] -> IO [a]
emitOne :: a -> IO [a] -> IO [a]
emitOne  a
x  IO [a]
io = do [a]
ys <- IO [a] -> IO [a]
forall a. IO a -> IO a
unsafeInterleaveIO IO [a]
io
                    [a] -> IO [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
ys)
emitMany :: [a] -> IO [a] -> IO [a]
emitMany [a]
xs IO [a]
io = do [a]
ys <- IO [a] -> IO [a]
forall a. IO a -> IO a
unsafeInterleaveIO IO [a]
io
                    [a] -> IO [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ([a]
xs[a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++[a]
ys)


----
gatherDefined :: Posn -> SymTab HashDefine -> String -> IO Bool
gatherDefined :: Posn -> SymTab HashDefine -> FilePath -> IO Bool
gatherDefined Posn
p SymTab HashDefine
st FilePath
inp =
  case Parser Char FilePath
-> FilePath -> (Either FilePath FilePath, FilePath)
forall t a. Parser t a -> [t] -> (Either FilePath a, [t])
runParser (SymTab HashDefine -> Parser Char FilePath
preExpand SymTab HashDefine
st) FilePath
inp of
    (Left FilePath
msg, FilePath
_) -> FilePath -> IO Bool
forall a. HasCallStack => FilePath -> a
error (FilePath
"Cannot expand #if directive in file "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p
                           FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
":\n    "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
msg)
    (Right FilePath
s, FilePath
xs) -> do
--      hPutStrLn stderr $ "Expanded #if at "++show p++" is:\n  "++s
        Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) FilePath
xs) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
             Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr (FilePath
"Warning: trailing characters after #if"
                              FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
" macro expansion in file "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
pFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
": "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
xs)

        case Parser Char Bool -> FilePath -> (Either FilePath Bool, FilePath)
forall t a. Parser t a -> [t] -> (Either FilePath a, [t])
runParser Parser Char Bool
parseBoolExp FilePath
s of
          (Left FilePath
msg, FilePath
_) -> FilePath -> IO Bool
forall a. HasCallStack => FilePath -> a
error (FilePath
"Cannot parse #if directive in file "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
p
                                 FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
":\n    "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
msg)
          (Right Bool
b, FilePath
xs) -> do Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) FilePath
xs Bool -> Bool -> Bool
&& FilePath -> Bool
notComment FilePath
xs) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
                                   Handle -> FilePath -> IO ()
hPutStrLn Handle
stderr
                                     (FilePath
"Warning: trailing characters after #if"
                                      FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
" directive in file "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++Posn -> FilePath
forall a. Show a => a -> FilePath
show Posn
pFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
": "FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
xs)
                              Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
b

notComment :: FilePath -> Bool
notComment = Bool -> Bool
not (Bool -> Bool) -> (FilePath -> Bool) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath
"//"FilePath -> FilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf`) (FilePath -> Bool) -> (FilePath -> FilePath) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isSpace


-- | The preprocessor must expand all macros (recursively) before evaluating
--   the conditional.
preExpand :: SymTab HashDefine -> TextParser String
preExpand :: SymTab HashDefine -> Parser Char FilePath
preExpand SymTab HashDefine
st =
  do  Parser Char ()
forall t. Parser t ()
eof
      FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
""
  Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  FilePath
a <- Parser Char Char -> Parser Char FilePath
forall (p :: * -> *) a. PolyParse p => p a -> p [a]
many1 ((Char -> Bool) -> Parser Char Char
forall t. (t -> Bool) -> Parser t t
satisfy Char -> Bool
notIdent)
      Parser Char FilePath -> Parser Char FilePath
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (Parser Char FilePath -> Parser Char FilePath)
-> Parser Char FilePath -> Parser Char FilePath
forall a b. (a -> b) -> a -> b
$ (FilePath -> FilePath) -> Parser Char (FilePath -> FilePath)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FilePath
aFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++) Parser Char (FilePath -> FilePath)
-> Parser Char FilePath -> Parser Char FilePath
forall (p :: * -> *) a b. PolyParse p => p (a -> b) -> p a -> p b
`apply` SymTab HashDefine -> Parser Char FilePath
preExpand SymTab HashDefine
st
  Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  FilePath
b <- SymTab HashDefine -> Parser Char FilePath
expandSymOrCall SymTab HashDefine
st
      Parser Char FilePath -> Parser Char FilePath
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (Parser Char FilePath -> Parser Char FilePath)
-> Parser Char FilePath -> Parser Char FilePath
forall a b. (a -> b) -> a -> b
$ (FilePath -> FilePath) -> Parser Char (FilePath -> FilePath)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FilePath
bFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++) Parser Char (FilePath -> FilePath)
-> Parser Char FilePath -> Parser Char FilePath
forall (p :: * -> *) a b. PolyParse p => p (a -> b) -> p a -> p b
`apply` SymTab HashDefine -> Parser Char FilePath
preExpand SymTab HashDefine
st

-- | Expansion of symbols.
expandSymOrCall :: SymTab HashDefine -> TextParser String
expandSymOrCall :: SymTab HashDefine -> Parser Char FilePath
expandSymOrCall SymTab HashDefine
st =
  do FilePath
sym <- Parser Char FilePath
parseSym
     if FilePath
symFilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
==FilePath
"defined" then do FilePath
arg <- Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip Parser Char FilePath
parseSym; FilePath -> [FilePath] -> Parser Char FilePath
convert FilePath
sym [FilePath
arg]
                            Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
                            do FilePath
arg <- Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (Parser Char FilePath -> Parser Char FilePath)
-> Parser Char FilePath -> Parser Char FilePath
forall a b. (a -> b) -> a -> b
$ Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
parenthesis (do FilePath
x <- Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip Parser Char FilePath
parseSym;
                                                             Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
x))
                               FilePath -> [FilePath] -> Parser Char FilePath
convert FilePath
sym [FilePath
arg]
                            Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> FilePath -> [FilePath] -> Parser Char FilePath
convert FilePath
sym []
      else
      ( do  [FilePath]
args <- TextParser [FilePath] -> TextParser [FilePath]
forall a. TextParser a -> TextParser a
parenthesis (TextParser [FilePath] -> TextParser [FilePath]
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (TextParser [FilePath] -> TextParser [FilePath])
-> TextParser [FilePath] -> TextParser [FilePath]
forall a b. (a -> b) -> a -> b
$ Parser Char FilePath
fragment Parser Char FilePath
-> Parser Char FilePath -> TextParser [FilePath]
forall (p :: * -> *) a sep. PolyParse p => p a -> p sep -> p [a]
`sepBy` Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
","))
            [FilePath]
args' <- ((FilePath -> Parser Char FilePath)
 -> [FilePath] -> TextParser [FilePath])
-> [FilePath]
-> (FilePath -> Parser Char FilePath)
-> TextParser [FilePath]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (FilePath -> Parser Char FilePath)
-> [FilePath] -> TextParser [FilePath]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM [FilePath]
args ((FilePath -> Parser Char FilePath) -> TextParser [FilePath])
-> (FilePath -> Parser Char FilePath) -> TextParser [FilePath]
forall a b. (a -> b) -> a -> b
$ \FilePath
arg->
                         case Parser Char FilePath
-> FilePath -> (Either FilePath FilePath, FilePath)
forall t a. Parser t a -> [t] -> (Either FilePath a, [t])
runParser (SymTab HashDefine -> Parser Char FilePath
preExpand SymTab HashDefine
st) FilePath
arg of
                             (Left FilePath
msg, FilePath
_) -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail FilePath
msg
                             (Right FilePath
s, FilePath
_)  -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
s
            FilePath -> [FilePath] -> Parser Char FilePath
convert FilePath
sym [FilePath]
args'
        Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> FilePath -> [FilePath] -> Parser Char FilePath
convert FilePath
sym []
      )
  where
    fragment :: Parser Char FilePath
fragment = Parser Char Char -> Parser Char FilePath
forall (p :: * -> *) a. PolyParse p => p a -> p [a]
many1 ((Char -> Bool) -> Parser Char Char
forall t. (t -> Bool) -> Parser t t
satisfy (Char -> FilePath -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem`FilePath
",)"))
    convert :: FilePath -> [FilePath] -> Parser Char FilePath
convert FilePath
"defined" [FilePath
arg] =
      case FilePath -> SymTab HashDefine -> Maybe HashDefine
forall v. FilePath -> SymTab v -> Maybe v
lookupST FilePath
arg SymTab HashDefine
st of
        Maybe HashDefine
Nothing | (Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit FilePath
arg    -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
arg 
        Maybe HashDefine
Nothing                      -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"0"
        Just (a :: HashDefine
a@AntiDefined{})       -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"0"
        Just (a :: HashDefine
a@SymbolReplacement{}) -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"1"
        Just (a :: HashDefine
a@MacroExpansion{})    -> FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"1"
    convert FilePath
sym [FilePath]
args =
      case FilePath -> SymTab HashDefine -> Maybe HashDefine
forall v. FilePath -> SymTab v -> Maybe v
lookupST FilePath
sym SymTab HashDefine
st of
        Maybe HashDefine
Nothing  -> if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
args then FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
sym
                    else FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"0"
                 -- else fail (disp sym args++" is not a defined macro")
        Just (a :: HashDefine
a@SymbolReplacement{}) -> do FilePath -> Parser Char ()
forall t. [t] -> Parser t ()
reparse (HashDefine -> FilePath
replacement HashDefine
a)
                                           FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
""
        Just (a :: HashDefine
a@MacroExpansion{})    -> do FilePath -> Parser Char ()
forall t. [t] -> Parser t ()
reparse (HashDefine -> [FilePath] -> Bool -> FilePath
expandMacro HashDefine
a [FilePath]
args Bool
False)
                                           FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
""
        Just (a :: HashDefine
a@AntiDefined{})       ->
                    if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
args then FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
sym
                    else FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"0"
                 -- else fail (disp sym args++" explicitly undefined with -U")
    disp :: FilePath -> t a -> FilePath
disp FilePath
sym t a
args = let len :: Int
len = t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
args
                        chars :: [FilePath]
chars = (Char -> FilePath) -> FilePath -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map (Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:[]) [Char
'a'..Char
'z']
                    in FilePath
sym FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ if t a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null t a
args then FilePath
""
                              else FilePath
"("FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath -> [FilePath] -> FilePath
forall a. [a] -> [[a]] -> [a]
intercalate FilePath
"," (Int -> [FilePath] -> [FilePath]
forall a. Int -> [a] -> [a]
take Int
len [FilePath]
chars)FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
")"

parseBoolExp :: TextParser Bool
parseBoolExp :: Parser Char Bool
parseBoolExp =
  do  Bool
a <- Parser Char Bool
parseExp1
      [Bool]
bs <- Parser Char Bool -> Parser Char [Bool]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (do Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"||")
                     Parser Char Bool -> Parser Char Bool
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (Parser Char Bool -> Parser Char Bool)
-> Parser Char Bool -> Parser Char Bool
forall a b. (a -> b) -> a -> b
$ Parser Char Bool -> Parser Char Bool
forall a. TextParser a -> TextParser a
skip Parser Char Bool
parseBoolExp)
      Bool -> Parser Char Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Parser Char Bool) -> Bool -> Parser Char Bool
forall a b. (a -> b) -> a -> b
$ (Bool -> Bool -> Bool) -> Bool -> [Bool] -> Bool
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Bool -> Bool -> Bool
(||) Bool
a [Bool]
bs

parseExp1 :: TextParser Bool
parseExp1 :: Parser Char Bool
parseExp1 =
  do  Bool
a <- Parser Char Bool
parseExp0
      [Bool]
bs <- Parser Char Bool -> Parser Char [Bool]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (do Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"&&")
                     Parser Char Bool -> Parser Char Bool
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (Parser Char Bool -> Parser Char Bool)
-> Parser Char Bool -> Parser Char Bool
forall a b. (a -> b) -> a -> b
$ Parser Char Bool -> Parser Char Bool
forall a. TextParser a -> TextParser a
skip Parser Char Bool
parseExp1)
      Bool -> Parser Char Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Parser Char Bool) -> Bool -> Parser Char Bool
forall a b. (a -> b) -> a -> b
$ (Bool -> Bool -> Bool) -> Bool -> [Bool] -> Bool
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Bool -> Bool -> Bool
(&&) Bool
a [Bool]
bs

parseExp0 :: TextParser Bool
parseExp0 :: Parser Char Bool
parseExp0 =
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"!")
      Bool
a <- Parser Char Bool -> Parser Char Bool
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (Parser Char Bool -> Parser Char Bool)
-> Parser Char Bool -> Parser Char Bool
forall a b. (a -> b) -> a -> b
$ Parser Char Bool
parseExp0
      Bool -> Parser Char Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Bool
not Bool
a)
  Parser Char Bool -> Parser Char Bool -> Parser Char Bool
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Integer
val1 <- TextParser Integer
parseArithExp1
      Integer -> Integer -> Bool
op   <- TextParser (Integer -> Integer -> Bool)
parseCmpOp
      Integer
val2 <- TextParser Integer
parseArithExp1
      Bool -> Parser Char Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
val1 Integer -> Integer -> Bool
`op` Integer
val2)
  Parser Char Bool -> Parser Char Bool -> Parser Char Bool
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Integer
sym <- TextParser Integer
parseArithExp1
      case Integer
sym of
        Integer
0 -> Bool -> Parser Char Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
        Integer
_ -> Bool -> Parser Char Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
  Parser Char Bool -> Parser Char Bool -> Parser Char Bool
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char Bool -> Parser Char Bool
forall a. TextParser a -> TextParser a
parenthesis (Parser Char Bool -> Parser Char Bool
forall (p :: * -> *) a. Commitment p => p a -> p a
commit Parser Char Bool
parseBoolExp)

parseArithExp1 :: TextParser Integer
parseArithExp1 :: TextParser Integer
parseArithExp1 =
  do  Integer
val1 <- TextParser Integer
parseArithExp0
      ( do Integer -> Integer -> Integer
op   <- TextParser (Integer -> Integer -> Integer)
parseArithOp1
           Integer
val2 <- TextParser Integer
parseArithExp1
           Integer -> TextParser Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
val1 Integer -> Integer -> Integer
`op` Integer
val2)
        TextParser Integer -> TextParser Integer -> TextParser Integer
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Integer -> TextParser Integer
forall (m :: * -> *) a. Monad m => a -> m a
return Integer
val1 )
  TextParser Integer -> TextParser Integer -> TextParser Integer
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  TextParser Integer -> TextParser Integer
forall a. TextParser a -> TextParser a
parenthesis TextParser Integer
parseArithExp1

parseArithExp0 :: TextParser Integer
parseArithExp0 :: TextParser Integer
parseArithExp0 =
  do  Integer
val1 <- TextParser Integer
parseNumber
      ( do Integer -> Integer -> Integer
op   <- TextParser (Integer -> Integer -> Integer)
parseArithOp0
           Integer
val2 <- TextParser Integer
parseArithExp0
           Integer -> TextParser Integer
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
val1 Integer -> Integer -> Integer
`op` Integer
val2)
        TextParser Integer -> TextParser Integer -> TextParser Integer
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Integer -> TextParser Integer
forall (m :: * -> *) a. Monad m => a -> m a
return Integer
val1 )
  TextParser Integer -> TextParser Integer -> TextParser Integer
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  TextParser Integer -> TextParser Integer
forall a. TextParser a -> TextParser a
parenthesis TextParser Integer
parseArithExp0

parseNumber :: TextParser Integer
parseNumber :: TextParser Integer
parseNumber = (FilePath -> Integer) -> Parser Char FilePath -> TextParser Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FilePath -> Integer
safeRead (Parser Char FilePath -> TextParser Integer)
-> Parser Char FilePath -> TextParser Integer
forall a b. (a -> b) -> a -> b
$ Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip Parser Char FilePath
parseSym
  where
    safeRead :: FilePath -> Integer
safeRead FilePath
s =
      case FilePath
s of
        Char
'0':Char
'x':FilePath
s' -> (FilePath -> [(Integer, FilePath)]) -> FilePath -> Integer
forall t b. (t -> [(Integer, b)]) -> t -> Integer
number FilePath -> [(Integer, FilePath)]
forall a. (Eq a, Num a) => ReadS a
readHex FilePath
s'
        Char
'0':Char
'o':FilePath
s' -> (FilePath -> [(Integer, FilePath)]) -> FilePath -> Integer
forall t b. (t -> [(Integer, b)]) -> t -> Integer
number FilePath -> [(Integer, FilePath)]
forall a. (Eq a, Num a) => ReadS a
readOct FilePath
s'
        FilePath
_          -> (FilePath -> [(Integer, FilePath)]) -> FilePath -> Integer
forall t b. (t -> [(Integer, b)]) -> t -> Integer
number FilePath -> [(Integer, FilePath)]
forall a. (Eq a, Num a) => ReadS a
readDec FilePath
s
    number :: (t -> [(Integer, b)]) -> t -> Integer
number t -> [(Integer, b)]
rd t
s =
      case t -> [(Integer, b)]
rd t
s of
        []        -> Integer
0 :: Integer
        ((Integer
n,b
_):[(Integer, b)]
_) -> Integer
n :: Integer

parseCmpOp :: TextParser (Integer -> Integer -> Bool)
parseCmpOp :: TextParser (Integer -> Integer -> Bool)
parseCmpOp =
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
">=")
      (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(>=)
  TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
">")
      (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(>)
  TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"<=")
      (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(<=)
  TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"<")
      (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
(<)
  TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"==")
      (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
(==)
  TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"!=")
      (Integer -> Integer -> Bool)
-> TextParser (Integer -> Integer -> Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
(/=)

parseArithOp1 :: TextParser (Integer -> Integer -> Integer)
parseArithOp1 :: TextParser (Integer -> Integer -> Integer)
parseArithOp1 =
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"+")
      (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(+)
  TextParser (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"-")
      (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (m :: * -> *) a. Monad m => a -> m a
return (-)

parseArithOp0 :: TextParser (Integer -> Integer -> Integer)
parseArithOp0 :: TextParser (Integer -> Integer -> Integer)
parseArithOp0 =
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"*")
      (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (m :: * -> *) a. Monad m => a -> m a
return Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(*)
  TextParser (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"/")
      (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
div)
  TextParser (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
"%")
      (Integer -> Integer -> Integer)
-> TextParser (Integer -> Integer -> Integer)
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
rem)

-- | Return the expansion of the symbol (if there is one).
parseSymOrCall :: SymTab HashDefine -> TextParser String
parseSymOrCall :: SymTab HashDefine -> Parser Char FilePath
parseSymOrCall SymTab HashDefine
st =
  do  FilePath
sym <- Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip Parser Char FilePath
parseSym
      [FilePath]
args <- TextParser [FilePath] -> TextParser [FilePath]
forall a. TextParser a -> TextParser a
parenthesis (TextParser [FilePath] -> TextParser [FilePath]
forall (p :: * -> *) a. Commitment p => p a -> p a
commit (TextParser [FilePath] -> TextParser [FilePath])
-> TextParser [FilePath] -> TextParser [FilePath]
forall a b. (a -> b) -> a -> b
$ SymTab HashDefine -> Parser Char FilePath
parseSymOrCall SymTab HashDefine
st Parser Char FilePath
-> Parser Char FilePath -> TextParser [FilePath]
forall (p :: * -> *) a sep. PolyParse p => p a -> p sep -> p [a]
`sepBy` Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip (FilePath -> Parser Char FilePath
isWord FilePath
","))
      FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath -> Parser Char FilePath)
-> FilePath -> Parser Char FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> [FilePath] -> FilePath
convert FilePath
sym [FilePath]
args
  Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  do  FilePath
sym <- Parser Char FilePath -> Parser Char FilePath
forall a. TextParser a -> TextParser a
skip Parser Char FilePath
parseSym
      FilePath -> Parser Char FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath -> Parser Char FilePath)
-> FilePath -> Parser Char FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> [FilePath] -> FilePath
convert FilePath
sym []
  where
    convert :: FilePath -> [FilePath] -> FilePath
convert FilePath
sym [FilePath]
args =
      case FilePath -> SymTab HashDefine -> Maybe HashDefine
forall v. FilePath -> SymTab v -> Maybe v
lookupST FilePath
sym SymTab HashDefine
st of
        Maybe HashDefine
Nothing  -> FilePath
sym
        Just (a :: HashDefine
a@SymbolReplacement{}) -> SymTab HashDefine -> FilePath -> FilePath
recursivelyExpand SymTab HashDefine
st (HashDefine -> FilePath
replacement HashDefine
a)
        Just (a :: HashDefine
a@MacroExpansion{})    -> SymTab HashDefine -> FilePath -> FilePath
recursivelyExpand SymTab HashDefine
st (HashDefine -> [FilePath] -> Bool -> FilePath
expandMacro HashDefine
a [FilePath]
args Bool
False)
        Just (a :: HashDefine
a@AntiDefined{})       -> HashDefine -> FilePath
name HashDefine
a

recursivelyExpand :: SymTab HashDefine -> String -> String
recursivelyExpand :: SymTab HashDefine -> FilePath -> FilePath
recursivelyExpand SymTab HashDefine
st FilePath
inp =
  case Parser Char FilePath
-> FilePath -> (Either FilePath FilePath, FilePath)
forall t a. Parser t a -> [t] -> (Either FilePath a, [t])
runParser (SymTab HashDefine -> Parser Char FilePath
parseSymOrCall SymTab HashDefine
st) FilePath
inp of
    (Left FilePath
msg, FilePath
_) -> FilePath
inp
    (Right FilePath
s,  FilePath
_) -> FilePath
s

parseSym :: TextParser String
parseSym :: Parser Char FilePath
parseSym = Parser Char Char -> Parser Char FilePath
forall (p :: * -> *) a. PolyParse p => p a -> p [a]
many1 ((Char -> Bool) -> Parser Char Char
forall t. (t -> Bool) -> Parser t t
satisfy (\Char
c-> Char -> Bool
isAlphaNum Char
c Bool -> Bool -> Bool
|| Char
cChar -> FilePath -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem`FilePath
"'`_"))
           Parser Char FilePath
-> Parser Char FilePath -> Parser Char FilePath
forall t a. Parser t a -> Parser t a -> Parser t a
`onFail`
           do FilePath
xs <- Parser Char FilePath
allAsString
              FilePath -> Parser Char FilePath
forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail (FilePath -> Parser Char FilePath)
-> FilePath -> Parser Char FilePath
forall a b. (a -> b) -> a -> b
$ FilePath
"Expected an identifier, got \""FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
xsFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
"\""

notIdent :: Char -> Bool
notIdent :: Char -> Bool
notIdent Char
c = Bool -> Bool
not (Char -> Bool
isAlphaNum Char
c Bool -> Bool -> Bool
|| Char
cChar -> FilePath -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem`FilePath
"'`_")

skip :: TextParser a -> TextParser a
skip :: TextParser a -> TextParser a
skip TextParser a
p = Parser Char Char -> Parser Char FilePath
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many ((Char -> Bool) -> Parser Char Char
forall t. (t -> Bool) -> Parser t t
satisfy Char -> Bool
isSpace) Parser Char FilePath -> TextParser a -> TextParser a
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> TextParser a
p

-- | The standard "parens" parser does not work for us here.  Define our own.
parenthesis :: TextParser a -> TextParser a
parenthesis :: TextParser a -> TextParser a
parenthesis TextParser a
p = do FilePath -> Parser Char FilePath
isWord FilePath
"("
                   a
x <- TextParser a
p
                   FilePath -> Parser Char FilePath
isWord FilePath
")"
                   a -> TextParser a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x

-- | Determine filename in \#include
file :: SymTab HashDefine -> String -> String
file :: SymTab HashDefine -> FilePath -> FilePath
file SymTab HashDefine
st FilePath
name =
    case FilePath
name of
      (Char
'"':FilePath
ns) -> FilePath -> FilePath
forall a. [a] -> [a]
init FilePath
ns
      (Char
'<':FilePath
ns) -> FilePath -> FilePath
forall a. [a] -> [a]
init FilePath
ns
      FilePath
_ -> let ex :: FilePath
ex = SymTab HashDefine -> FilePath -> FilePath
recursivelyExpand SymTab HashDefine
st FilePath
name in
           if FilePath
ex FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
name then FilePath
name else SymTab HashDefine -> FilePath -> FilePath
file SymTab HashDefine
st FilePath
ex