ministg-0.1: src/Ministg/AST.hs
-----------------------------------------------------------------------------
-- |
-- Module : Ministg.AST
-- Copyright : (c) 2009 Bernie Pope
-- License : BSD-style
-- Maintainer : bjpop@csse.unimelb.edu.au
-- Stability : experimental
-- Portability : ghc
--
-- The representation of the abstract syntax tree for ministg programs.
-----------------------------------------------------------------------------
module Ministg.AST where
import Prelude
import Ministg.CallStack (CallStack, prettyCallStack)
import Ministg.Pretty
import Data.Set as Set hiding (map)
-- | Variables (also known as identifiers).
type Var = String
-- | Data constructor names.
type Constructor = String
class FreeVars t where
freeVars :: t -> Set Var
instance FreeVars t => FreeVars [t] where
freeVars = Set.unions . map freeVars
-- | Literal integers. These correspond to unboxed integers in the semantics.
data Literal = Integer Integer
deriving (Eq, Show)
instance Pretty Literal where
pretty (Integer i) = pretty i
-- | Atomic expressions.
data Atom
= Literal Literal -- ^ Literal values (unoboxed integers).
| Variable Var -- ^ Variables.
deriving (Eq, Show)
instance Pretty Atom where
pretty (Literal l) = pretty l
pretty (Variable v) = text v
instance FreeVars Atom where
freeVars (Literal {}) = Set.empty
freeVars (Variable v) = Set.singleton v
-- | Is an atom a literal?
isLiteral :: Atom -> Bool
isLiteral (Literal {}) = True
isLiteral _other = False
-- | The arity (number of parameters) of a function. It is only known when the function
-- being applied is statically known (not lambda bound).
type FunArity = Maybe Int
prettyArity :: FunArity -> Doc
prettyArity Nothing = text "_?"
prettyArity (Just i) = text "_" <> int i
-- | Expressions.
data Exp
= Atom Atom -- ^ Atomic expressions (literals, variables).
| FunApp FunArity Var [Atom] -- ^ Function application (f^k a_1 ... a_n, n >= 1).
| PrimApp Prim [Atom] -- ^ Saturated primitive application (op a_1 ... a_n, n >= 1).
| Let Var Object Exp -- ^ Let declaration.
| Case Exp [Alt] -- ^ Case expression.
| Stack String Exp -- ^ Like SCC, but just for stacks. (stack str (exp))
deriving (Eq, Show)
instance FreeVars Exp where
freeVars (Atom a) = freeVars a
freeVars (FunApp _arity var args) = Set.singleton var `Set.union` freeVars args
freeVars (PrimApp prim args) = freeVars args
-- Treat this as a letrec, which means that the var is bound (not free) in the object
freeVars (Let var object exp)
= Set.delete var (freeVars exp `Set.union` freeVars object)
freeVars (Case exp alts)
= freeVars exp `Set.union` freeVars alts
freeVars (Stack _str exp) = freeVars exp
instance Pretty Exp where
pretty (Atom a) = pretty a
pretty (FunApp arity var atoms) = text var <> prettyArity arity <+> hsep (map pretty atoms)
pretty (PrimApp prim atoms) = pretty prim <+> hsep (map pretty atoms)
pretty letExp@(Let var obj exp)
= maybeNest (text "let {") prettyDecls (rbrace <+> text "in" <+> pretty inExp)
where
(decls, inExp) = unflattenLet letExp
prettyDecls = vcat (punctuate semi (map pretty decls))
maybeNest letPart declPart inPart
| length decls < 2 = letPart <+> declPart <+> inPart
| otherwise = letPart $$ (nest 3 declPart) $$ inPart
pretty (Case exp alts) =
text "case" <+> pretty exp <+> text "of {" $$
nest 3 (vcat (punctuate semi (map pretty alts))) $$
rbrace
pretty (Stack annotation exp) =
maybeNest exp (text "stack" <+> doubleQuotes (text annotation)) (pretty exp)
isNestedExp :: Exp -> Bool
isNestedExp (Let {}) = True
isNestedExp (Case {}) = True
isNestedExp (Stack {}) = True
isNestedExp other = False
unflattenLet :: Exp -> ([Decl], Exp)
unflattenLet exp = unflattenLetAcc exp []
where
unflattenLetAcc :: Exp -> [Decl] -> ([Decl], Exp)
unflattenLetAcc (Let var obj exp) ds = unflattenLetAcc exp (Decl var obj : ds)
unflattenLetAcc exp ds = (reverse ds, exp)
-- | Case alternatives (the right-hand-sides of case branches).
data Alt
= PatAlt Constructor [Var] Exp -- ^ Constructor pattern (C x_1 ... x_n -> e, n >= 0).
| DefaultAlt Var Exp -- ^ Default pattern (matches anything) (x -> e).
deriving (Eq, Show)
instance FreeVars Alt where
freeVars (PatAlt constructor args exp) = freeVars exp \\ Set.fromList args
freeVars (DefaultAlt var exp) = Set.delete var $ freeVars exp
instance Pretty Alt where
pretty (PatAlt con vars exp) = maybeNest exp (text con <+> hsep (map text vars) <+> rightArrow) (pretty exp)
pretty (DefaultAlt var exp) = text var <+> rightArrow <+> pretty exp
rightArrow :: Doc
rightArrow = text "->"
-- | Objects. These serve two roles in the language:
--
-- (1) as part of the language syntax (except blackholes).
-- (2) as things which are allocated on the heap during execution.
data Object
= Fun [Var] Exp -- ^ Function values (FUN (x_1 ... x_n -> e).
| Pap Var [Atom] -- ^ Partial applications (PAP (f a_1 ... a_n)).
| Con Constructor [Atom] -- ^ Data constructor application (CON (C a_1 ... a_n)).
| Thunk Exp CallStack -- ^ THUNK (e).
| BlackHole -- ^ BLACKHOLE (only during evaluation - not part of the language syntax).
| Error -- ^ Raise an exception.
deriving (Eq, Show)
instance FreeVars Object where
freeVars (Fun vars exp) = freeVars exp \\ Set.fromList vars
freeVars (Pap var args) = Set.singleton var `Set.union` freeVars args
freeVars (Con constructor args) = freeVars args
freeVars (Thunk exp callStack) = freeVars exp
freeVars BlackHole = Set.empty
freeVars Error = Set.empty
maybeNest :: Exp -> Doc -> Doc -> Doc
maybeNest exp d1 d2 = if isNestedExp exp then d1 $$ (nest 3 d2) else d1 <+> d2
instance Pretty Object where
pretty (Fun vars exp)
= text "FUN" <> parens (maybeNest exp (hsep (map text vars) <+> rightArrow) (pretty exp))
pretty (Pap var atoms) = text "PAP" <> parens (text var <+> hsep (map pretty atoms))
pretty (Con constructor atoms) = text "CON" <> parens (text constructor <+> hsep (map pretty atoms))
pretty (Thunk exp callStack)
= text "THUNK" <> parens (pretty exp) $$ (nest 3 (prettyCallStack callStack))
pretty BlackHole = text "BLACKHOLE"
pretty Error = text "ERROR"
-- | Test for "value" objects.
isValue :: Object -> Bool
isValue (Fun {}) = True
isValue (Pap {}) = True
isValue (Con {}) = True
isValue _other = False
-- | Test for FUN objects
isFun :: Object -> Bool
isFun (Fun {}) = True
isFun other = False
-- | Test for PAP objects
isPap :: Object -> Bool
isPap (Pap {}) = True
isPap other = False
-- | A top-level declaration (f = obj).
data Decl = Decl Var Object
deriving Show
instance Pretty Decl where
pretty (Decl var obj) = text var <+> equals <+> pretty obj
-- | A whole program.
newtype Program = Program [Decl]
deriving Show
instance Pretty Program where
pretty (Program decls) = vcat (punctuate semi (map pretty decls))
-- | Primitive operators.
data Prim
= Add -- ^ Unboxed integer addition (x + y).
| Subtract -- ^ Unboxed integer subtraction (x - y).
| Multiply -- ^ Unboxed integer multiplication (x * y).
| Equality -- ^ Unboxed integer equality test (x == y).
| LessThan -- ^ Unboxed integer less-than comparison (x < y).
| GreaterThan -- ^ Unboxed integer greater-than comparison ( x > y).
| LessThanEquals -- ^ Unboxed integer less-than-equals comparison ( x <= y).
| GreaterThanEquals -- ^ Unboxed integer greater-than-equals comparison ( x >= y).
| IntToBool -- ^ Convert an unboxed integer to a (boxed) boolean ( 1 = True, 0 = False).
deriving (Eq, Show)
instance Pretty Prim where
pretty Add = text "plus#"
pretty Subtract = text "sub#"
pretty Multiply = text "mult#"
pretty Equality = text "eq#"
pretty LessThan = text "lt#"
pretty GreaterThan = text "gt#"
pretty LessThanEquals = text "lte#"
pretty GreaterThanEquals = text "gte#"
pretty IntToBool = text "intToBool#"