{-# LANGUAGE LambdaCase #-}

module Transformations.Anf.PrettyPrinter (prettyPrint) where

import Control.Monad.State (MonadState (get), State, evalState, modify)
import Data.Text (unpack)
import Transformations.Anf.Anf
import Trees.Common

prettyPrint :: Program -> String
prettyPrint :: Program -> String
prettyPrint (Program [GlobalDeclaration]
decls) = [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ GlobalDeclaration -> String
prettyDecl (GlobalDeclaration -> String) -> [GlobalDeclaration] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [GlobalDeclaration]
decls

type IndentState = State IndentLevel

type IndentLevel = Int

prettyDecl :: GlobalDeclaration -> String
prettyDecl :: GlobalDeclaration -> String
prettyDecl GlobalDeclaration
decl = State IdCnt String -> IdCnt -> String
forall s a. State s a -> s -> a
evalState (GlobalDeclaration -> State IdCnt String
prettyDecl' GlobalDeclaration
decl) IdCnt
0 String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
";;"
  where
    prettyDecl' :: GlobalDeclaration -> IndentState String
    prettyDecl' :: GlobalDeclaration -> State IdCnt String
prettyDecl' (GlobVarDecl Identifier'
name Expression
value) = do
      String
val' <- Expression -> State IdCnt String
prettyExpr Expression
value
      String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ [String] -> String
unwords [String
"let", Identifier' -> String
prettyId Identifier'
name, String
"=", String
val']
    prettyDecl' (GlobFunDecl Identifier'
name [Identifier']
params Expression
body) = do
      String
val' <- Expression -> State IdCnt String
prettyExpr Expression
body
      String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ [String] -> String
unwords [String
"let", Identifier' -> String
prettyId Identifier'
name, [String] -> String
unwords (Identifier' -> String
prettyId (Identifier' -> String) -> [Identifier'] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Identifier']
params), String
"=", String
val']

prettyExpr :: Expression -> IndentState String
prettyExpr :: Expression -> State IdCnt String
prettyExpr (ExprAtom AtomicExpression
aexpr) = String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ AtomicExpression -> String
prettyAtomic AtomicExpression
aexpr
prettyExpr (ExprComp ComplexExpression
cexpr) = ComplexExpression -> State IdCnt String
prettyComplex ComplexExpression
cexpr
prettyExpr (ExprLetIn (Identifier'
ident, Expression
val) Expression
expr) = do
  (IdCnt -> IdCnt) -> StateT IdCnt Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (IdCnt -> IdCnt -> IdCnt
forall a. Num a => a -> a -> a
+ IdCnt
2)
  IdCnt
indent <- StateT IdCnt Identity IdCnt
forall s (m :: * -> *). MonadState s m => m s
get
  String
val' <- Expression -> State IdCnt String
prettyExpr Expression
val
  String
expr' <- Expression -> State IdCnt String
prettyExpr Expression
expr
  let declText :: String
declText = IdCnt -> String
createIndent IdCnt
indent String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"let " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Identifier' -> String
prettyId Identifier'
ident String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" = " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
val'
  let exprText :: String
exprText = IdCnt -> String
createIndent IdCnt
indent String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"in " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
expr'
  (IdCnt -> IdCnt) -> StateT IdCnt Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((IdCnt -> IdCnt) -> StateT IdCnt Identity ())
-> (IdCnt -> IdCnt) -> StateT IdCnt Identity ()
forall a b. (a -> b) -> a -> b
$ \IdCnt
x -> IdCnt
x IdCnt -> IdCnt -> IdCnt
forall a. Num a => a -> a -> a
- IdCnt
2
  String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ String
declText String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
exprText

prettyComplex :: ComplexExpression -> IndentState String
prettyComplex :: ComplexExpression -> State IdCnt String
prettyComplex = \case
  CompApp Identifier'
f AtomicExpression
arg -> String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ String -> String
parens (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Identifier' -> String
prettyId Identifier'
f String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> AtomicExpression -> String
prettyAtomic AtomicExpression
arg
  CompIte AtomicExpression
c Expression
t Expression
e -> do
    (IdCnt -> IdCnt) -> StateT IdCnt Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (IdCnt -> IdCnt -> IdCnt
forall a. Num a => a -> a -> a
+ IdCnt
4)
    IdCnt
indent <- StateT IdCnt Identity IdCnt
forall s (m :: * -> *). MonadState s m => m s
get
    let cText :: String
cText = IdCnt -> String
createIndent (IdCnt
indent IdCnt -> IdCnt -> IdCnt
forall a. Num a => a -> a -> a
- IdCnt
2) String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"if " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> AtomicExpression -> String
prettyAtomic AtomicExpression
c
    String
t' <- Expression -> State IdCnt String
prettyExpr Expression
t
    let tText :: String
tText = IdCnt -> String
createIndent IdCnt
indent String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"then " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
t'
    String
e' <- Expression -> State IdCnt String
prettyExpr Expression
e
    let eText :: String
eText = IdCnt -> String
createIndent IdCnt
indent String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"else " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
e'
    (IdCnt -> IdCnt) -> StateT IdCnt Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((IdCnt -> IdCnt) -> StateT IdCnt Identity ())
-> (IdCnt -> IdCnt) -> StateT IdCnt Identity ()
forall a b. (a -> b) -> a -> b
$ \IdCnt
x -> IdCnt
x IdCnt -> IdCnt -> IdCnt
forall a. Num a => a -> a -> a
- IdCnt
4
    String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ String
cText String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
tText String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
eText
  CompBinOp BinaryOperator
op AtomicExpression
lhs AtomicExpression
rhs -> String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ String -> String
parens ([String] -> String
unwords [AtomicExpression -> String
prettyAtomic AtomicExpression
lhs, BinaryOperator -> String
prettyBinOp BinaryOperator
op, AtomicExpression -> String
prettyAtomic AtomicExpression
rhs])
  CompUnOp UnaryOperator
op AtomicExpression
x -> String -> State IdCnt String
forall a. a -> StateT IdCnt Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> State IdCnt String) -> String -> State IdCnt String
forall a b. (a -> b) -> a -> b
$ String -> String
parens (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ UnaryOperator -> String
prettyUnOp UnaryOperator
op String -> String -> String
forall a. Semigroup a => a -> a -> a
<> AtomicExpression -> String
prettyAtomic AtomicExpression
x

prettyAtomic :: AtomicExpression -> String
prettyAtomic :: AtomicExpression -> String
prettyAtomic = \case
  AtomId Identifier'
name -> Identifier' -> String
prettyId Identifier'
name
  AtomicExpression
AtomUnit -> String
"()"
  AtomBool Bool
value -> if Bool
value then String
"true" else String
"false"
  AtomInt Int64
value -> Int64 -> String
forall a. Show a => a -> String
show Int64
value

prettyId :: Identifier' -> String
prettyId :: Identifier' -> String
prettyId (Txt Identifier
n) = Identifier -> String
unpack Identifier
n
prettyId (Gen IdCnt
n Identifier
ident) = Identifier -> String
unpack Identifier
ident String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"'" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> IdCnt -> String
forall a. Show a => a -> String
show IdCnt
n

prettyBinOp :: BinaryOperator -> String
prettyBinOp :: BinaryOperator -> String
prettyBinOp = \case
  BoolOp BooleanOperator
AndOp -> String
"&&"
  BoolOp BooleanOperator
OrOp -> String
"||"
  ArithOp ArithmeticOperator
PlusOp -> String
"+"
  ArithOp ArithmeticOperator
MinusOp -> String
"-"
  ArithOp ArithmeticOperator
MulOp -> String
"*"
  ArithOp ArithmeticOperator
DivOp -> String
"/"
  CompOp ComparisonOperator
EqOp -> String
"="
  CompOp ComparisonOperator
NeOp -> String
"<>"
  CompOp ComparisonOperator
LtOp -> String
"<"
  CompOp ComparisonOperator
LeOp -> String
"<="
  CompOp ComparisonOperator
GtOp -> String
">"
  CompOp ComparisonOperator
GeOp -> String
">="

prettyUnOp :: UnaryOperator -> String
prettyUnOp :: UnaryOperator -> String
prettyUnOp UnaryOperator
UnMinusOp = String
"-"

createIndent :: Int -> String
createIndent :: IdCnt -> String
createIndent IdCnt
indent = String
"\n" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> IdCnt -> Char -> String
forall a. IdCnt -> a -> [a]
replicate IdCnt
indent Char
' '

parens :: String -> String
parens :: String -> String
parens String
val = String
"(" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
val String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
")"