module AST.Expression.General where
import AST.PrettyPrint
import Text.PrettyPrint as P
import qualified AST.Annotation as Annotation
import qualified AST.Helpers as Help
import qualified AST.Literal as Literal
import qualified AST.Pattern as Pattern
import qualified AST.Type as Type
import qualified AST.Variable as Var
type Expr annotation definition variable =
Annotation.Annotated annotation (Expr' annotation definition variable)
data Expr' ann def var
= Literal Literal.Literal
| Var var
| Range (Expr ann def var) (Expr ann def var)
| ExplicitList [Expr ann def var]
| Binop var (Expr ann def var) (Expr ann def var)
| Lambda (Pattern.Pattern var) (Expr ann def var)
| App (Expr ann def var) (Expr ann def var)
| MultiIf [(Expr ann def var,Expr ann def var)]
| Let [def] (Expr ann def var)
| Case (Expr ann def var) [(Pattern.Pattern var, Expr ann def var)]
| Data String [Expr ann def var]
| Access (Expr ann def var) String
| Remove (Expr ann def var) String
| Insert (Expr ann def var) String (Expr ann def var)
| Modify (Expr ann def var) [(String, Expr ann def var)]
| Record [(String, Expr ann def var)]
| Port (PortImpl (Expr ann def var) var)
| GLShader String String Literal.GLShaderTipe
deriving (Show)
data PortImpl expr var
= In String (Type.PortType var)
| Out String expr (Type.PortType var)
| Task String expr (Type.PortType var)
deriving (Show)
portName :: PortImpl expr var -> String
portName impl =
case impl of
In name _ -> name
Out name _ _ -> name
Task name _ _ -> name
rawVar :: String -> Expr' ann def Var.Raw
rawVar x =
Var (Var.Raw x)
localVar :: String -> Expr' ann def Var.Canonical
localVar x =
Var (Var.Canonical Var.Local x)
tuple :: [Expr ann def var] -> Expr' ann def var
tuple expressions =
Data ("_Tuple" ++ show (length expressions)) expressions
saveEnvName :: String
saveEnvName =
"_save_the_environment!!!"
dummyLet :: (Pretty def) => [def] -> Expr Annotation.Region def Var.Canonical
dummyLet defs =
Annotation.none $ Let defs (Annotation.none $ Var (Var.builtin saveEnvName))
instance (Pretty def, Pretty var, Var.ToString var) => Pretty (Expr' ann def var) where
pretty expression =
case expression of
Literal literal ->
pretty literal
Var x ->
pretty x
Range lowExpr highExpr ->
P.brackets (pretty lowExpr <> P.text ".." <> pretty highExpr)
ExplicitList elements ->
P.brackets (commaCat (map pretty elements))
Binop op (Annotation.A _ (Literal (Literal.IntNum 0))) expr
| Var.toString op == "-" ->
P.text "-" <> prettyParens expr
Binop op leftExpr rightExpr ->
P.hang (prettyParens leftExpr) 2 (P.text op'' <+> prettyParens rightExpr)
where
op' = Var.toString op
op'' = if Help.isOp op' then op' else "`" ++ op' ++ "`"
Lambda pattern expr ->
P.text "\\" <> args <+> P.text "->" <+> pretty body
where
(patterns, body) = collectLambdas expr
args = P.sep (map Pattern.prettyParens (pattern : patterns))
App expr arg ->
P.hang func 2 (P.sep args)
where
func:args =
map prettyParens (collectApps expr ++ [arg])
MultiIf branches ->
P.text "if" $$ nest 3 (vcat $ map iff branches)
where
iff (b,e) = P.text "|" <+> P.hang (pretty b <+> P.text "->") 2 (pretty e)
Let defs body ->
P.sep
[ P.hang (P.text "let") 4 (P.vcat (map pretty defs))
, P.text "in" <+> pretty body
]
Case expr branches ->
P.hang pexpr 2 (P.vcat (map pretty' branches))
where
pexpr = P.sep [ P.text "case" <+> pretty expr, P.text "of" ]
pretty' (pattern, branch) =
pretty pattern <+> P.text "->" <+> pretty branch
Data "::" [hd,tl] ->
pretty hd <+> P.text "::" <+> pretty tl
Data "[]" [] ->
P.text "[]"
Data name exprs
| Help.isTuple name ->
P.parens (commaCat (map pretty exprs))
| otherwise ->
P.hang (P.text name) 2 (P.sep (map prettyParens exprs))
Access record field ->
prettyParens record <> P.text "." <> variable field
Remove record field ->
P.braces (pretty record <+> P.text "-" <+> variable field)
Insert (Annotation.A _ (Remove record y)) x v ->
P.braces $
P.hsep
[ pretty record, P.text "-", variable y, P.text "|"
, variable x, P.equals, pretty v
]
Insert record field expr ->
P.braces (pretty record <+> P.text "|" <+> variable field <+> P.equals <+> pretty expr)
Modify record fields ->
P.braces $
P.hang
(pretty record <+> P.text "|")
4
(commaSep $ map field fields)
where
field (k,v) = variable k <+> P.text "<-" <+> pretty v
Record fields ->
P.sep
[ P.cat (zipWith (<+>) (P.lbrace : repeat P.comma) (map field fields))
, P.rbrace
]
where
field (name, expr) =
variable name <+> P.equals <+> pretty expr
GLShader _ _ _ ->
P.text "[glsl| ... |]"
Port portImpl ->
pretty portImpl
instance (Pretty expr, Pretty var) => Pretty (PortImpl expr var) where
pretty impl =
P.text ("<port:" ++ portName impl ++ ">")
collectApps :: Expr ann def var -> [Expr ann def var]
collectApps annExpr@(Annotation.A _ expr) =
case expr of
App a b -> collectApps a ++ [b]
_ -> [annExpr]
collectLambdas :: Expr ann def var -> ([Pattern.Pattern var], Expr ann def var)
collectLambdas lexpr@(Annotation.A _ expr) =
case expr of
Lambda pattern body ->
let (ps, body') = collectLambdas body
in (pattern : ps, body')
_ -> ([], lexpr)
prettyParens :: (Pretty def, Pretty var, Var.ToString var) => Expr ann def var -> Doc
prettyParens (Annotation.A _ expr) =
parensIf needed (pretty expr)
where
needed =
case expr of
Binop _ _ _ -> True
Lambda _ _ -> True
App _ _ -> True
MultiIf _ -> True
Let _ _ -> True
Case _ _ -> True
Data name (_:_) -> not (name == "::" || Help.isTuple name)
_ -> False