packages feed

syntactic-0.7: CEFP/Imperative/Imperative.hs

module Imperative.Imperative where



import Data.List



type Ident = String

data Expr
    = Lit String           -- Literal
    | Var Ident            -- Variable
    | App String [Expr]    -- Function call
    | Op String Expr Expr  -- Binary operator

type IsPar = Bool  --  Parallel or sequential loop?

data Stmt
    = Nop                        -- No-op
    | Ident := Expr              -- Assignment
    | Cond Expr Prog Prog        -- Conditional
    | For IsPar Expr Ident Prog  -- For loop

type Prog = [Stmt]

data Main = Main
    { mainInp  :: [Ident]
    , mainBody :: Prog
    }

instance Show Main
  where
    show = renderMain



paren :: String -> String
paren = ("(" ++) . (++ ")")

-- | Like 'unlines', but without the final newline
unLines :: [String] -> String
unLines = concat . intersperse "\n"

indent :: Int -> String -> String
indent n = unLines . map move . lines
  where
    move = (replicate n ' ' ++)

renderExpr :: Expr -> String
renderExpr (Lit a)        = a
renderExpr (Var ident)    = ident
renderExpr (App str args) = paren $ unwords (str : map renderExpr args)
renderExpr (Op str a b)   = paren $ unwords [renderExpr a, str, renderExpr b]

mkNop :: Prog -> Prog
mkNop []   = [Nop]
mkNop prog = prog

renderStmt :: Stmt -> String
renderStmt Nop             = "nop"
renderStmt (ident := expr) = ident ++ " := " ++ renderExpr expr
renderStmt (Cond cond tHEN eLSE)
    | isSmall =
        ("if " ++ renderExpr cond ++ " then " ++ tRend ++ " else " ++ eRend)
    | otherwise =
        ("\nif " ++ renderExpr cond ++ " then\n")
          ++
        indent 2 tRend
          ++
        "\nelse\n"
          ++
        indent 2 eRend
  where
    t       = mkNop tHEN
    e       = mkNop eLSE
    tRend   = renderProg t
    eRend   = renderProg e
    isSmall = length (lines tRend) <= 1 && length (lines eRend) <= 1
renderStmt (For isPar len index body) =
    (loop ++ index ++ " in 0 .. (" ++ renderExpr len ++ "-1) do\n")
      ++
    indent 2 (renderProg body)
  where
    loop = if isPar then "\npar " else "\nfor "

renderProg :: Prog -> String
renderProg = unLines . map renderStmt

renderMain :: Main -> String
renderMain (Main params prog) =
    ("main (" ++ concat (intersperse "," params) ++ ")\n")
      ++
    indent 2 (renderProg prog)

printMain :: Main -> IO ()
printMain = putStrLn . (++"\n") . renderMain