{-  CSci 450: Organization of Programming Languages
    Imperative Core Language, Abstract Syntax
    Fall 2017 
    H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2017-09-14: Separated from earlier Imperative Core parser and
            evaluator prototypes
2017-09-21: Corrected Show functionality for "*".
            Changed import ValueImpCore to Values


The intention of this module is to encapsulate the abstract syntqx as
much as is practical -- such as to centralize the data type
definitions and the Show functionality.

However, given that the abstract syntax consists of algebraic data
type definitions, the semantics of the abstract syntax tree is known
by modules that must create (e.g., parser) and use (e.g., evaluator)
the abstract syntax trees.

TODO:
- Consider whether to have just Prim constructor for primitives

-}

module AbSynImpCore
    ( ValType, Name, Def(..), Expr(..) )
where

-- Haskell libraries
import Data.List (intercalate)

-- Imperative Core modules
import Values (ValType)

-- Type definitions
type Name = String


{-  TOP-LEVEL DEFINITIONS (AST)

    Kamin/Ramsey "use" difficult to embed using Haskell, so we may
    make it a REPL command.
-}

data Def = Val Name Expr
         | Define Name [Name] Expr
         | Top Expr
--       | Use Name

-- Define Show instance for Def to enable convenient string format
instance Show Def where
    show (Val n e)       = "(val " ++ n ++ " " ++ show e ++ ")"
    show (Define n fs e) =
        "(define " ++ n ++ " " ++ showNameList fs ++ " "
          ++ show e ++ ")"
    show (Top e)         = show e
--  show (Use n)         = "(use " ++ n ++ ")"  -- moved to REPL command

showNameList :: [Name] -> String
showNameList ns = "(" ++ (intercalate " " ns) ++ ")"
-- Note: May use library function intercalate for the first time


{-  LOWER-LEVEL EXPRESSIONS (AST)

    This does not have Kamin/Ramsey "print" primitive.
-}

data Expr = Lit ValType 
          | Var Name 
          | Add Expr Expr 
          | Sub Expr Expr 
          | Mul Expr Expr
          | Div Expr Expr 
          | Eq  Expr Expr
          | Lt  Expr Expr
          | Gt  Expr Expr
          | Print Expr
          | If  Expr Expr Expr
          | While Expr Expr
          | Begin [Expr]
          | Set Name Expr 
          | Apply Name [Expr]

-- Define Show instance for Expr to enable convenient string format
instance Show Expr where
    show (Lit v)       = show v
    show (Var n)       = n
    show (Add l r)     = showParExpr "+"     [l,r]
    show (Sub l r)     = showParExpr "-"     [l,r]
    show (Mul l r)     = showParExpr "*"     [l,r]
    show (Div l r)     = showParExpr "/"     [l,r]
    show (Eq  l r)     = showParExpr "=="    [l,r]
    show (Lt  l r)     = showParExpr "<"     [l,r]
    show (Gt  l r)     = showParExpr ">"     [l,r] 
    show (Print e)     = showParExpr "print" [e]
    show (If ce te ee) = showParExpr "if"    [ce,te,ee]
    show (While ce be) = showParExpr "while" [ce,be]
    show (Begin es)    = showParExpr "begin" es
    show (Set n e)     = "(set " ++ n ++ " " ++ show e ++ ")"
    show (Apply n es)  =
        "(" ++ n ++ (if null es then "" else " " ++ showExprList es) ++ ")"

showParExpr :: String -> [Expr] -> String
showParExpr op es = "(" ++ op ++ " " ++ showExprList es ++ ")"
    
showExprList :: [Expr] -> String
showExprList es = intercalate " " (map show es)

