packages feed

baskell-0.1: src/AST.hs

{-------------------------------------------------------------------------------

        Copyright:              Bernie Pope 2004

        Module:                 AST

        Description:            Definition of the Abstract Syntax Tree for
                                Baskell programs.

        Primary Authors:        Bernie Pope

-------------------------------------------------------------------------------}

{-
    This file is part of baskell.

    baskell is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    baskell is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with baskell; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

module AST
   ( Ident
   , Exp (..)
   , Lit (..)
   , Decl (..)
   , Program (..)
   , emptyProgram
   , isEmptyProgram
   , (=:)
   , var
   , lam
   , (@@)
   , app
   , litI
   , litC
   , litB
   , list
   , tup
   , prim
   ) where

import Pretty
   ( parensIf
   , char
   , text
   , (<>)
   , comma
   , Doc
   , (<+>)
   , vcat
   , parens
   , cat
   , render
   , punctuate
   , int
   , Pretty (..)
   )

import Data.List (sort)

import Type (Type)

--------------------------------------------------------------------------------

-- identifiers
type Ident = String

-- expressions
data Exp
   = Var Ident
   | Lam Ident Exp
   | LamStrict Ident Exp
   | App Exp Exp
   | Literal Lit
   | Tuple [Exp]
   | Prim String (Exp -> Maybe Exp)
   deriving Show

instance Show (a -> b) where
   show f = "<function>"

-- literals
data Lit
   = LitInt  Int
   | LitChar Char
   | LitBool Bool
   | LitCons
   | LitNil
   deriving Show

-- declarations
data Decl
   = Decl Ident Exp
   | Sig Ident Type
   deriving Show

instance Eq Decl where
   (Decl ident1 _body1) == (Decl ident2 _body2) = ident1 == ident2
   (Sig ident1 _ty1) == (Sig ident2 _ty2) = ident1 == ident2

instance Ord Decl where
   compare (Decl ident1 _body1) (Decl ident2 _body2)
      = compare ident1 ident2
   compare (Sig ident1 _ty1) (Sig ident2 _ty2)
      = compare ident1 ident2

-- programs
newtype Program
   = Program [Decl]
   deriving Show

emptyProgram :: Program
emptyProgram = Program []

isEmptyProgram :: Program -> Bool
isEmptyProgram (Program decls)
   = null decls

--------------------------------------------------------------------------------

-- pretty printing of the AST

instance Pretty Exp where
   pretty exp = prettyExp False exp

-- this should be a reader monad
prettyExp :: Bool -> Exp -> Doc
prettyExp _parens (Var ident) = text ident
prettyExp parens (Lam ident exp)
   = parensIf parens
        $ char '\\' <>
          text ident <+>
          text "->" <+>
          pretty exp
prettyExp parens (LamStrict ident exp)
   = parensIf parens
        $ char '!' <>
          text ident <+>
          text "->" <+>
          pretty exp
prettyExp _parens exp@(App (App (Literal LitCons) _hd) _tl)
   = prettyList exp
prettyExp parens exp@(App e1 e2)
   = parensIf parens
        $ prettyExp (not $ isApp e1) e1 <+> prettyExp True e2
   where
   isApp :: Exp -> Bool
   isApp (App _ _) = True
   isApp other     = False
prettyExp _parens (Literal lit)
   = pretty lit
prettyExp _parens (Tuple exps)
   = parens $ cat (punctuate comma $ map pretty exps)
prettyExp _parens (Prim name _implementation)
   = text "prim_" <> text name

instance Pretty Lit where
   pretty (LitInt i)  = pretty i
   pretty (LitChar c) = text $ show c
   pretty (LitBool b) = text $ show b
   pretty LitCons     = text $ "Cons"
   pretty LitNil      = text $ "[]"

instance Pretty Decl where
   pretty (Decl name body) = text name <+> text "=" <+> pretty body
   pretty (Sig name ty)    = text name <+> text "::" <+> pretty ty

instance Pretty Program where
   pretty (Program decls) = vcat $ map pretty (sort decls)

prettyList :: Exp -> Doc
prettyList (Literal LitNil) = text "[]"
prettyList (App (App (Literal LitCons) x) tail)
   = char '[' <> pretty x <> prettyList' tail
   where
   prettyList' (Literal LitNil) = char ']'
   prettyList' (App (App (Literal LitCons) h) t)
      = comma <> pretty h <> prettyList' t
   prettyList' other = comma <> pretty other


--------------------------------------------------------------------------------

-- below are some convenience functions that
-- make writing AST values slighlty nicer.

infix 0 =:
(=:) :: Ident -> Exp -> Decl
(=:) = Decl

var :: Ident -> Exp
var = Var

lam :: [Ident] -> Exp -> Exp
lam xs e = foldr Lam e xs

infixl 9 @@
(@@) :: Exp -> Exp -> Exp
e1 @@ e2 = App e1 e2

app :: [Exp] -> Exp
app [] = error "app applied to empty list"
app xs = foldl1 App xs

litI :: Int -> Exp
litI = Literal . LitInt

litC :: Char -> Exp
litC = Literal . LitChar

litB :: Bool -> Exp
litB = Literal . LitBool

litLC :: Exp
litLC = Literal LitCons

litLN :: Exp
litLN = Literal LitNil

list :: [Exp] -> Exp
list = foldr cons litLN

cons :: Exp -> Exp -> Exp
cons hd tl = litLC @@ hd @@ tl

tup :: [Exp] -> Exp
tup = Tuple

prim :: String -> (Exp -> Maybe Exp) -> Exp
prim = Prim