tools/pas2c/PascalPreprocessor.hs
branchsdl2transition
changeset 11362 ed5a6478e710
parent 10240 bfae7354d42f
child 15988 24545642473f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/pas2c/PascalPreprocessor.hs	Tue Nov 10 20:43:13 2015 +0100
@@ -0,0 +1,138 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module PascalPreprocessor where
+
+import Text.Parsec
+import Control.Monad.IO.Class
+import Control.Monad
+import System.IO
+import qualified Data.Map as Map
+import qualified Control.Exception as E
+
+char' :: Char -> ParsecT String u IO ()
+char' = void . char
+
+string' :: String -> ParsecT String u IO ()
+string' = void . string
+
+-- comments are removed
+comment :: ParsecT String u IO String
+comment = choice [
+        char '{' >> notFollowedBy (char '$') >> manyTill anyChar (try $ char '}') >> return ""
+        , (try $ string "(*") >> manyTill anyChar (try $ string "*)") >> return ""
+        , (try $ string "//") >> manyTill anyChar (try newline) >> return "\n"
+        ]
+
+preprocess :: String -> String -> String -> [String] -> IO String
+preprocess inputPath alternateInputPath fn symbols = do
+    r <- runParserT (preprocessFile (inputPath ++ fn)) (Map.fromList $ map (\s -> (s, "")) symbols, [True]) "" ""
+    case r of
+         (Left a) -> do
+             hPutStrLn stderr (show a)
+             return ""
+         (Right a) -> return a
+
+    where
+    preprocessFile fn' = do
+        f <- liftIO (readFile fn')
+        setInput f
+        preprocessor
+
+    preprocessor, codeBlock, switch :: ParsecT String (Map.Map String String, [Bool]) IO String
+
+    preprocessor = chainr codeBlock (return (++)) ""
+
+    codeBlock = do
+        s <- choice [
+            switch
+            , comment
+            , char '\'' >> many (noneOf "'\n") >>= \s -> char '\'' >> return ('\'' : s ++ "'")
+            , identifier >>= replace
+            , noneOf "{" >>= \a -> return [a]
+            ]
+        (_, ok) <- getState
+        return $ if and ok then s else ""
+
+    --otherChar c = c `notElem` "{/('_" && not (isAlphaNum c)
+    identifier = do
+        c <- letter <|> oneOf "_"
+        s <- many (alphaNum <|> oneOf "_")
+        return $ c:s
+
+    switch = do
+        try $ string' "{$"
+        s <- choice [
+            include
+            , ifdef
+            , if'
+            , elseSwitch
+            , endIf
+            , define
+            , unknown
+            ]
+        return s
+
+    include = do
+        try $ string' "INCLUDE"
+        spaces
+        (char' '"')
+        ifn <- many1 $ noneOf "\"\n"
+        char' '"'
+        spaces
+        char' '}'
+        f <- liftIO (readFile (inputPath ++ ifn) 
+            `E.catch` (\(_ :: E.IOException) -> readFile (alternateInputPath ++ ifn) 
+                `E.catch` (\(_ :: E.IOException) -> error $ "File not found: " ++ ifn)
+                )
+            )
+        c <- getInput
+        setInput $ f ++ c
+        return ""
+
+    ifdef = do
+        s <- try (string "IFDEF") <|> try (string "IFNDEF")
+        let f = if s == "IFNDEF" then not else id
+
+        ds <- (spaces >> identifier) `sepBy` (spaces >> string "OR")
+        spaces
+        char' '}'
+
+        updateState $ \(m, b) ->
+            (m, (f $ any (flip Map.member m) ds) : b)
+
+        return ""
+
+    if' = do
+        try (string' "IF" >> notFollowedBy alphaNum)
+
+        void $ manyTill anyChar (char' '}')
+        --char '}'
+
+        updateState $ \(m, b) ->
+            (m, False : b)
+
+        return ""
+
+    elseSwitch = do
+        try $ string' "ELSE}"
+        updateState $ \(m, b:bs) -> (m, (not b):bs)
+        return ""
+    endIf = do
+        try $ string' "ENDIF}"
+        updateState $ \(m, _:bs) -> (m, bs)
+        return ""
+    define = do
+        try $ string' "DEFINE"
+        spaces
+        i <- identifier
+        d <- ((string ":=" >> return ()) <|> spaces) >> many (noneOf "}")
+        char' '}'
+        updateState $ \(m, b) -> (if (and b) && (head i /= '_') then Map.insert i d m else m, b)
+        return ""
+    replace s = do
+        (m, _) <- getState
+        return $ Map.findWithDefault s s m
+
+    unknown = do
+        un <- many1 $ noneOf "}\n"
+        char' '}'
+        return $ "{$" ++ un ++ "}"