couple more use after frees for gear deletion.
also rearranged lines a bit and added a redundant exit to try to proof things a bit.
more evidence we need rust.
{-# LANGUAGE ScopedTypeVariables #-}module PascalPreprocessor whereimport Text.Parsecimport Control.Monad.IO.Classimport Control.Monadimport System.IOimport qualified Data.Map as Mapimport qualified Control.Exception as Echar' :: Char -> ParsecT String u IO ()char' = void . charstring' :: String -> ParsecT String u IO ()string' = void . string-- comments are removedcomment :: ParsecT String u IO Stringcomment = 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 Stringpreprocess 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 ++ "}"