packages feed

uuagc-0.9.54: src-ag/PrintCode.ag

PRAGMA strictdata
PRAGMA optimize
PRAGMA bangpats
PRAGMA strictwrap

INCLUDE "Code.ag"
INCLUDE "Patterns.ag"

imports
{
import Data.Char (isAlphaNum)
import Pretty
import Code
import Options
import CommonTypes (attrname, _LOC, nullIdent)
import Data.List(intersperse)
import System.IO
import System.Directory
import System.FilePath
import CommonTypes(BlockInfo, BlockKind(..))
}

{
type PP_Docs = [PP_Doc]
}

{
ppMultiSeqH :: [PP_Doc] -> PP_Doc -> PP_Doc
ppMultiSeqH = ppMultiSeq' (>#<)

ppMultiSeqV :: [PP_Doc] -> PP_Doc -> PP_Doc
ppMultiSeqV = ppMultiSeq' (>-<)

ppMultiSeq' :: (PP_Doc -> PP_Doc -> PP_Doc) -> [PP_Doc] -> PP_Doc -> PP_Doc
ppMultiSeq' next strictArgs expr
  = foldr (\v r -> (v >#< "`seq`") `next` pp_parens r) expr strictArgs
}

ATTR Expr Exprs Decl Decls CaseAlt CaseAlts Lhs [ outputfile : {String} | | ]

SEM Chunk
  | Chunk
      loc.outputfile = if sepSemMods @lhs.options
                       then replaceBaseName @lhs.mainFile (takeBaseName @lhs.mainFile ++ "_" ++ @name)
                       else @lhs.mainFile

ATTR Program                            [ options:{Options} | | output:{PP_Docs} ]

ATTR Expr Exprs Decl Decls Chunk Chunks CaseAlts CaseAlt Lhs Pattern Patterns [ options:{Options} | | ]

ATTR Expr Decl DataAlt Type NamedType Lhs [ nested:{Bool} | | pp:{PP_Doc} ]

ATTR Exprs DataAlts Types NamedTypes Decls Chunk Chunks [ nested:{Bool} | | pps : {PP_Docs} ]

ATTR CaseAlt CaseAlts                   [ nested:{Bool} | | pps: {PP_Docs} ]

SEM Program | Program
  loc.options = @lhs.options { breadthFirst = breadthFirst @lhs.options && visit @lhs.options && cases @lhs.options && @ordered }

SEM Program
  | Program chunks.nested = nest @lhs.options

SEM Exprs
  | Cons lhs.pps = @hd.pp : @tl.pps
  | Nil  lhs.pps = []

SEM CaseAlts
  | Cons lhs.pps = @hd.pps ++ @tl.pps
  | Nil  lhs.pps = []

SEM DataAlts
  | Cons lhs.pps = @hd.pp : @tl.pps
  | Nil  lhs.pps = []

SEM Types
  | Cons lhs.pps = @hd.pp : @tl.pps
  | Nil  lhs.pps = []

SEM NamedTypes
  | Cons lhs.pps = @hd.pp : @tl.pps
  | Nil  lhs.pps = []

SEM Decls
  | Cons lhs.pps = @hd.pp : @tl.pps
  | Nil  lhs.pps = []

SEM Chunks
  | Cons lhs.pps = @hd.pps ++ @tl.pps
  | Nil  lhs.pps = []


SEM Program
  | Program    lhs.output  = @chunks.pps

SEM Chunk
  | Chunk      lhs.pps     =  @comment.pp
                              :  @info.pps
                              ++ @dataDef.pps
                              ++ @cataFun.pps
                              ++ @semDom.pps
                              ++ @semWrapper.pps
                              ++ @semFunctions.pps
                              ++ [Map.findWithDefault empty (BlockOther, Just $ identifier @name) @lhs.textBlockMap]

SEM Decl
  | Decl       lhs.pp      = @left.pp >#< "="
                             >-< indent 4 @rhs.pp
  | Bind       lhs.pp      = @left.pp >#< "<-" >#< @rhs.pp
  | BindLet    lhs.pp      = "let" >#< @left.pp >#< "=" >#< @rhs.pp
  | Data       lhs.pp      = "data" >#< hv_sp (@name : @params)
                             >#<  ( case @alts.pps of
                                          [] -> empty
                                          (x:xs) ->              "=" >#<  x
                                                 >-< vlist (map ("|" >#<) xs)
                                     >-< if null @derivings
                                            then empty
                                            else "deriving" >#< ppTuple False (map text @derivings)
                                  )
  | NewType    lhs.pp      = "newtype" >#< hv_sp (@name : @params) >#< "=" >#< @con >#< pp_parens @tp.pp
  | Type       lhs.pp      = "type" >#< hv_sp (@name : @params) >#< "=" >#<  @tp.pp
  | TSig       lhs.pp      = @name >#< "::" >#< @tp.pp
  | Comment    lhs.pp      = if '\n' `elem` @txt
                               then "{-" >-< vlist (lines @txt) >-< "-}"
                               else "--" >#< @txt
  | PragmaDecl lhs.pp      = "{-#" >#< text @txt >#< "#-}"
  | Resume     lhs.pp      = if @monadic
                             then @left.pp >#< "<-" >#< @rhs.pp
                             else @left.pp >#< "=" >-< indent 4 @rhs.pp
  | EvalDecl   loc.strat   = if breadthFirstStrict @lhs.options
                             then "stepwiseEval"
                             else "lazyEval"
               lhs.pp      = if breadthFirst @lhs.options
                             then @left.pp >#< "=" >#< "case" >#< @loc.strat >#< pp_parens @rhs.pp >#< "of"
                                  >-< indent 4 (
                                    pp_parens (@nt >|< "_Syn" >#< "_val") >#< "-> _val"
                                  )
                             else @left.pp >#< "=" >#< @rhs.pp

SEM Expr
  | Let        lhs.pp      = pp_parens (    "let" >#< (vlist @decls.pps)
                                       >-< "in " >#< @body.pp
                                       )
  | Case       lhs.pp      = pp_parens (    "case" >#< pp_parens @expr.pp >#< "of"
                                       >-< (vlist @alts.pps)
                                       )
  | Do         lhs.pp      = pp_parens ( "do" >#< (   vlist @stmts.pps
                                                  >-< ("return" >#< @body.pp))
                                       )
  | Lambda     loc.strictParams = if strictSems @lhs.options
                                  then @args.pps
                                  else []
               loc.addBang = if bangpats @lhs.options
                             then \p -> pp_parens ("!" >|< p)
                             else id
               lhs.pp      = pp_parens (    "\\" >#< (vlist (map @loc.addBang @args.pps)) >#< "->"
                                       >-< indent 4 (@loc.strictParams `ppMultiSeqV` @body.pp)
                                       )
  | TupleExpr  lhs.pp      = ppTuple @lhs.nested @exprs.pps
  | UnboxedTupleExpr  lhs.pp      = ppUnboxedTuple @lhs.nested @exprs.pps
  | App        lhs.pp      = pp_parens $ @name >#< hv_sp @args.pps
  | SimpleExpr lhs.pp      = text @txt
  | TextExpr   lhs.pp      = vlist (map text @lns)
  | Trace      lhs.pp      = "trace" >#< (   pp_parens ("\"" >|< text @txt >|< "\"")
                                         >-< pp_parens @expr.pp
                                         )
  | PragmaExpr lhs.pp      = let pragmaDoc = "{-#" >#< @txt >#< "#-}"
                                 op = if @onNewLine
                                      then (>-<)
                                      else (>#<)
                                 leftOp x y = if @onLeftSide
                                              then x `op` y
                                              else y
                                 rightOp x y = if @onLeftSide
                                               then x
                                               else x `op` y
                             in pp_parens (pragmaDoc `leftOp` @expr.pp `rightOp` pragmaDoc)
  | LineExpr   lhs.pp      = @expr.pp >-< "{-# LINE" >#< ppWithLineNr (\n -> pp $ show $ n + 1) >#< show @lhs.outputfile >#< "#-}"
                                      >-< ""
  | TypedExpr  lhs.pp      = pp_parens (@expr.pp >#< "::" >#< @tp.pp)
  | ResultExpr lhs.pp      = if breadthFirst @lhs.options
                             then "final" >#<
                                  pp_parens (@nt >|< "_Syn" >#< pp_parens @expr.pp)
                             else @expr.pp
  | InvokeExpr lhs.pp      = if breadthFirst @lhs.options
                             then "invoke" >#< pp_parens @expr.pp >#< pp_parens (
                                   @nt >|< "_Inh" >#< pp_parens (ppTuple False @args.pps))
                             else @expr.pp >#< hv_sp @args.pps
  | ResumeExpr lhs.pp      = if breadthFirst @lhs.options
                             then pp_parens ("resume" >#< pp_parens @expr.pp
                                            >-< indent 2 (pp_parens ( "\\" >|<
                                                  pp_parens ("~" >|< pp_parens (@nt >|< "_Syn" >#< "_inh_arg"))
                                                    >#< "->"
                                            >-< indent 2 ( "let" >#< @left.pp >#< "= _inh_arg"
                                            >-< indent 2 ("in" >#< @rhs.pp)
                                            ))))
                             else pp_parens ( "case" >#< pp_parens @expr.pp >#< "of"
                                            >-< ("{" >#< @left.pp >#< "->")
                                            >-< indent 4 (@rhs.pp >#< "}")
                                            )
  | SemFun     loc.strictParams = if strictSems @lhs.options
                                  then @args.pps
                                  else []
               loc.addBang = if bangpats @lhs.options
                             then \p -> pp_parens ("!" >|< p)
                             else id
               lhs.pp      = if breadthFirst @lhs.options
                             then "Child" >#< pp_parens ( "\\" >|<
                                      pp_parens (@nt >|< "_Inh" >#<
                                        ppTuple False (map @loc.addBang @args.pps)) >#< "->"
                                  >-< indent 2 (@loc.strictParams `ppMultiSeqV` @body.pp))
                             else if null @args.pps
                                  then @body.pp
                                  else pp_parens (    "\\" >#< (vlist (map @loc.addBang @args.pps)) >#< "->"
                                                 >-< indent 4 (@loc.strictParams `ppMultiSeqV` @body.pp)
                                                 )

SEM CaseAlt
  | CaseAlt    lhs.pps     = ["{" >#< @left.pp >#< "->", @expr.pp >#< "}"]

SEM DataAlt
  | DataAlt    lhs.pp      = @name >#< hv_sp (map ((@lhs.strictPre >|<) . pp_parens) @args.pps)
  | Record     lhs.pp      = @name >#< pp_block "{" "}" "," @args.pps

SEM NamedType
  | Named      lhs.pp      = if @strict
                             then @name >#< "::" >#< "!" >|< pp_parens @tp.pp
                             else @name >#< "::" >#< @tp.pp

SEM Lhs
  | Pattern3 TupleLhs UnboxedTupleLhs
      loc.addStrictGuard = if strictCases @lhs.options && @loc.hasStrictVars then \v -> v >#< "|" >#< @loc.strictGuard else id
  | Pattern3
      loc.strictGuard = @pat3.strictVars `ppMultiSeqH` (pp "True")
      loc.hasStrictVars = not (null @pat3.strictVars)
  | TupleLhs UnboxedTupleLhs
      loc.strictGuard = if stricterCases @lhs.options && not @lhs.isDeclOfLet
                        then map text @comps `ppMultiSeqH` (pp "True")
                        else pp "True"
      loc.hasStrictVars = not (null @comps)

  | Fun
      loc.addStrictGuard = if strictSems @lhs.options && @loc.hasStrictVars then \v -> v >#< "|" >#< @loc.strictGuard else id
      loc.hasStrictVars  = not (null @args.pps)
      loc.strictGuard    = @args.pps `ppMultiSeqH` (pp "True")

  | TupleLhs UnboxedTupleLhs Fun
      loc.addBang = if bangpats @lhs.options
                             then \p -> "!" >|< p
                             else id

  | Pattern3   lhs.pp      = @loc.addStrictGuard @pat3.pp
  | Pattern3SM lhs.pp      = @pat3.pp'
  | TupleLhs   lhs.pp      = @loc.addStrictGuard $ ppTuple @lhs.nested (map (@loc.addBang . text) @comps)
  | UnboxedTupleLhs   lhs.pp      = @loc.addStrictGuard $ ppUnboxedTuple @lhs.nested (map (@loc.addBang . text) @comps)
  | Fun        lhs.pp      = @loc.addStrictGuard (@name >#< hv_sp (map @loc.addBang @args.pps))
  | Unwrap     lhs.pp      = pp_parens (@name >#< @sub.pp)

SEM Type  [ | | prec:Int ]
  | Arr        lhs.prec    = 2
                  .pp      = @loc.l >#< "->" >-< @loc.r
               loc.l       = if @left.prec  <= 2 then pp_parens @left.pp  else @left.pp
                  .r       = if @right.prec <  2 then pp_parens @right.pp else @right.pp

  | TypeApp
      lhs.pp = hv_sp (@func.pp : @args.pps)

  | CtxApp
      lhs.pp = (pp_block "(" ")" "," $ map (\(n,ns) -> hv_sp $ map pp (n:ns)) @left) >#< "=>" >#< @right.pp
  | QuantApp
      lhs.pp = @left >#< @right.pp

  | TupleType  lhs.prec    = 5
                  .pp      = ppTuple @lhs.nested @tps.pps

  | UnboxedTupleType  lhs.prec    = 5
                         .pp      = ppUnboxedTuple @lhs.nested @tps.pps

  | List       lhs.prec    = 5
                  .pp      = "[" >|< @tp.pp >|< "]"

  | SimpleType lhs.prec    = 5
                  .pp      = if reallySimple @txt then text @txt else pp_parens (text @txt)

  | NontermType  lhs.prec  = 5
                 lhs.pp    = @loc.prefix >|< text @name >#< hv_sp @params
                 loc.prefix = if @deforested
                              then text "T_"
                              else empty
  | TMaybe       lhs.prec  = 5
                 lhs.pp    = text "Maybe" >#< pp_parens @tp.pp
  | TEither      lhs.prec  = 5
                 lhs.pp    = text "Either" >#< pp_parens @left.pp >#< pp_parens @right.pp
  | TMap         lhs.prec  = 5
                 lhs.pp    = text "Data.Map.Map" >#< pp_parens @key.pp >#< pp_parens @value.pp
  | TIntMap      lhs.prec  = 5
                 lhs.pp    = text "Data.IntMap.IntMap" >#< pp_parens @value.pp
  | TSet         lhs.prec  = 5
                 lhs.pp    = text "Data.Set.Set" >#< pp_parens @tp.pp
  | TIntSet      lhs.prec  = 5
                 lhs.pp    = text "Data.IntSet.IntSet"


{

reallySimple :: String -> Bool
reallySimple = and . map (\x -> isAlphaNum x || x=='_')

ppTuple :: Bool -> [PP_Doc] -> PP_Doc
ppTuple True  pps = "(" >|< pp_block " " (replicate (length pps `max` 1) ')') ",(" pps
ppTuple False pps = "(" >|< pp_block " " ")" "," pps
ppUnboxedTuple :: Bool -> [PP_Doc] -> PP_Doc
ppUnboxedTuple True pps  = "(# " >|< pp_block " " (concat $ replicate (length pps `max` 1) " #)") ",(# " pps
ppUnboxedTuple False pps = "(# " >|< pp_block " " " #)" "," pps

}


-------------------------------------------------------------------------------
--         Strict data fields
-------------------------------------------------------------------------------

ATTR DataAlt DataAlts [ strictPre: PP_Doc | | ]

SEM Decl
  | Data alts.strictPre = if @strict then pp "!" else empty

-------------------------------------------------------------------------------
--         Strict variables
-------------------------------------------------------------------------------

ATTR Pattern Patterns [ | | strictVars USE {++} {[]} : {[PP_Doc]} ]
SEM Pattern
  | Alias
      loc.strictVar
        = if strictCases @lhs.options && not @lhs.isDeclOfLet
          then [@loc.ppVar]
          else []
      loc.strictPatVars
        = if stricterCases @lhs.options && not @lhs.isDeclOfLet
          then @pat.strictVars
          else []
      lhs.strictVars
        = @loc.strictVar ++ @loc.strictPatVars
  | Irrefutable
      lhs.strictVars = []

-------------------------------------------------------------------------------
--         Pretty printing patterns
-------------------------------------------------------------------------------

SEM Patterns [ | | pps : {[PP_Doc]} ]
  | Cons lhs.pps = @hd.pp : @tl.pps
  | Nil  lhs.pps = []

SEM Pattern
  | Constr Product Alias
      loc.addBang = if bangpats @lhs.options && not @lhs.isDeclOfLet && not @lhs.belowIrrefutable
                    then \p -> "!" >|< p
                    else id

SEM Pattern [ | | pp:PP_Doc ]
  | Constr  lhs.pp     = @loc.addBang $ pp_parens $ @name >#< hv_sp @pats.pps
  | Product lhs.pp     = @loc.addBang $ pp_block "(" ")" "," @pats.pps
  | Alias   loc.ppVar  = pp (attrname @lhs.options False @field @attr)
            loc.ppVarBang = @loc.addBang $ @loc.ppVar
            lhs.pp     = if @pat.isUnderscore
                          then @loc.ppVarBang
                          else @loc.ppVarBang >|< "@" >|< @pat.pp
  | Irrefutable lhs.pp = text "~" >|< pp_parens @pat.pp
  | Underscore lhs.pp  = text "_"

SEM Pattern [ | | isUnderscore:{Bool}]
 | Constr      lhs.isUnderscore = False
 | Product     lhs.isUnderscore = False
 | Alias       lhs.isUnderscore = False
 | Underscore  lhs.isUnderscore = True

ATTR Pattern Patterns [ belowIrrefutable : Bool | | ]
SEM Pattern
  | Irrefutable
      pat.belowIrrefutable = True

SEM Lhs
  | Pattern3 Pattern3SM
      pat3.belowIrrefutable = False

-------------------------------------------------------------------------------
--         Pretty printing patterns for SM
-------------------------------------------------------------------------------

SEM Patterns [ | | pps' : {[PP_Doc]} ]
  | Cons lhs.pps' = @hd.pp' : @tl.pps'
  | Nil  lhs.pps' = []

SEM Pattern [ | | pp':PP_Doc ]
  | Constr  lhs.pp'     = pp_parens $ @name >#< hv_sp (map pp_parens @pats.pps')
  | Product lhs.pp'     = pp_block "(" ")" "," @pats.pps'
  | Alias   lhs.pp'     = let attribute | @field == _LOC || @field == nullIdent = locname' @attr
                                        | otherwise                             = attrname @lhs.options False @field @attr
                          in attribute >|< "@" >|< @pat.pp'
  | Irrefutable lhs.pp' = text "~" >|< pp_parens @pat.pp
  | Underscore lhs.pp'  = text "_"

{
locname' :: Identifier -> [Char]
locname' n = "_loc_" ++ getName n
}

-------------------------------------------------------------------------------
--         Determine if inside a Let
-------------------------------------------------------------------------------

ATTR Chunks Chunk Decls Decl Lhs Pattern Patterns [ isDeclOfLet : Bool | | ]
SEM Program
  | Program
      chunks.isDeclOfLet = False

SEM Expr
  | Let
      decls.isDeclOfLet = True
  | Do
      stmts.isDeclOfLet = False
  | ResumeExpr
      left.isDeclOfLet = False

SEM CaseAlt
  | CaseAlt
      left.isDeclOfLet = False



-------------------------------------------------------------------------------
--         Alternative code printing to separate modules
-------------------------------------------------------------------------------

ATTR Program [ mainBlocksDoc : PP_Doc | | genIO : {IO ()} ]
ATTR Program Chunks Chunk
  [ importBlocks : PP_Doc
    pragmaBlocks : String
    textBlocks : PP_Doc
    textBlockMap : {Map BlockInfo PP_Doc}
    optionsLine : String
    mainFile : String
    mainName : String
    moduleHeader : {String -> String -> String -> Bool -> String}
     | | ]

SEM Program
  | Program
      loc.mainModuleFile = @lhs.mainFile
      loc.genMainModule
        = writeModule @loc.mainModuleFile
            ( [ pp $ @lhs.pragmaBlocks
              , pp $ @lhs.optionsLine
              , pp $ @lhs.moduleHeader @lhs.mainName "" "" False
              , pp $ ("import " ++ @lhs.mainName ++ "_common\n")
              ]
              ++ map pp @chunks.imports
              ++ map vlist @chunks.appendMain
              ++ [@lhs.mainBlocksDoc]
            )

      loc.commonFile = replaceBaseName @lhs.mainFile (takeBaseName @lhs.mainFile ++ "_common")
      loc.genCommonModule
        = writeModule @loc.commonFile
              ( [ pp $ @lhs.pragmaBlocks
                , pp $ @lhs.optionsLine
                , pp $ @lhs.moduleHeader @lhs.mainName "_common" "" True
                , @lhs.importBlocks
                , @lhs.textBlocks
                ]
                ++ map vlist @chunks.appendCommon
              )

      lhs.genIO = do @loc.genMainModule
                     @loc.genCommonModule
                     @chunks.genSems

{
renderDocs :: [PP_Doc] -> String
renderDocs pps = foldr (.) id (map (\d -> (disp d 50000) . ( '\n':) ) pps) ""
}

ATTR Chunk Chunks [ | | imports USE {++} {[]} : {[String]} ]
SEM Chunk
  | Chunk
      lhs.imports = ["import " ++ @lhs.mainName ++ "_" ++ @name ++ "\n"]

ATTR Chunk Chunks [ | | appendCommon, appendMain USE {++} {[]} : {[[PP_Doc]]} ]

SEM Chunk
  | Chunk
      lhs.appendCommon
        = [ [@comment.pp]
          , @dataDef.pps
          , @semDom.pps
          , if reference @lhs.options then @semWrapper.pps else []
          ]
      lhs.appendMain
        = [ [@comment.pp]
          , @cataFun.pps
          , if reference @lhs.options then [] else @semWrapper.pps
          ]

ATTR Chunk Chunks [ | | genSems USE {>>} {return ()} : {IO ()} ]

SEM Chunk
  | Chunk
      lhs.genSems
        = writeModule @loc.outputfile
            [ pp $ @lhs.pragmaBlocks
            , pp $ Map.findWithDefault empty (BlockPragma, Just $ identifier @name) @lhs.textBlockMap
            , pp $ @lhs.optionsLine
            , pp $ @lhs.moduleHeader @lhs.mainName ("_" ++ @name) @loc.exports True
            , pp $ ("import " ++ @lhs.mainName ++ "_common\n")
            , pp $ Map.findWithDefault empty (BlockImport, Just $ identifier @name) @lhs.textBlockMap
            , @comment.pp
            , vlist_sep "" @info.pps
            , vlist_sep "" @semFunctions.pps
            , Map.findWithDefault empty (BlockOther, Just $ identifier @name) @lhs.textBlockMap
            ]

SEM Chunk
  | Chunk
      loc.exports = concat $ intersperse "," @semNames

{
writeModule :: FilePath -> [PP_Doc] -> IO ()
writeModule path docs
  = do bExists <- doesFileExist path
       if bExists
        then do input <- readFile path
                seq (length input) (return ())
                if input /= output
                 then dumpIt
                 else return ()
        else dumpIt
  where
    output = renderDocs docs
    dumpIt = writeFile path output
}