Search code examples
haskelluniplate

zip AST with bool list


I have an AST representing a haskell program and a bitvector/bool list representing the presence of strictness annotations on Patterns in order.For example, 1000 represents a program with 4 Pats where the first one is a BangPat. Is there any way that I can turn on and off the annotations in the AST according to the list?

-- EDIT: further clarify what I want editBang to do

Based on user5042's answer: Simple.hs :=

main = do
  case args of
    [] -> error "blah"
    [!x] -> putStrLn "one"
    (!x : xs) -> putStrLn "many"

And I want editBang "Simple.hs" [True, True, True, True] to produce

main = do
  case args of
    [] -> error "blah"
    [!x] -> putStrLn "one"
    (!(!x : !xs)) -> putStrLn "many"

Given that above are the only 4 places that ! can appear


Solution

  • As a first step, here's how to use transformBi:

    import Data.Data
    import Control.Monad
    import Data.Generics.Uniplate.Data
    import Language.Haskell.Exts
    import Text.Show.Pretty (ppShow)
    
    changeNames x = transformBi change x
      where change (Ident str) = Ident ("foo_" ++ str)
            change x           = x
    
    test2 = do
      content <- readFile "Simple.hs"
      case parseModule content of
        ParseFailed _ e -> error e
        ParseOk a       -> do
          let a' = changeNames a
          putStrLn $ ppShow a'
    

    The changeNames function finds all occurrences of a Ident s and replaces it with Ident ("foo_"++s) in the source tree.

    There is a monadic version called transformBiM which allows the replacement function to be monadic which would allow you to consume elements from your list of Bools as you found bang patterns.

    Here is a complete working example:

    import Control.Monad
    import Data.Generics.Uniplate.Data
    import Language.Haskell.Exts
    import Text.Show.Pretty (ppShow)
    import Control.Monad.State.Strict
    
    parseHaskell path = do
      content <- readFile path
      let mode = ParseMode path Haskell2010 [EnableExtension BangPatterns] False False Nothing
      case parseModuleWithMode mode content of
        ParseFailed _ e -> error $ path ++ ": " ++ e
        ParseOk a       -> return a
    
    changeBangs bools x = runState (transformBiM go x) bools
      where go pp@(PBangPat p) = do
               (b:bs) <- get
               put bs
               if b
                 then return p
                 else return pp
            go x = return x
    
    test = do
      a <- parseHaskell "Simple.hs"
      putStrLn $ unlines . map ("before: " ++) . lines $ ppShow a
      let a' = changeBangs [True,False] a
      putStrLn $ unlines . map ("after : " ++) . lines $ ppShow a'
    

    You might also look into using rewriteBiM.

    The file Simple.hs:

    main = do
      case args of
        [] -> error "blah"
        [!x] -> putStrLn "one"
        (!x : xs) -> putStrLn "many"