{-# OPTIONS_GHC -fglasgow-exts -fth -cpp #-}
module Emit.PIR.ParrotObject where

import Data.Char
import Emit.PIR
import Emit.Common
import Language.Haskell.TH.Syntax

type CnName = String
type Ty = String
data Cn = MkCn CnName [Ty]

genClass :: Ty -> [Cn] -> PIR
genClass ty cons = (genTy ty ++ concatMap (genCn ty) cons)

genNS :: Ty -> [Decl] -> Decl
genNS = DeclNS . genLit

genLit :: Ty -> String
genLit = ("PIR::" ++)

genTy :: Ty -> PIR
genTy ty =
    [ genNS ty
        [ DeclSub "__onload" [SubMETHOD] $ map StmtIns
            [ tempPMC <-- "newclass" $ [lit $ genLit ty]
            ]
        ]
    ]

genCn :: Ty -> Cn -> PIR
genCn ty (MkCn con tys) =
    [ genNS con
        [ DeclSub "__onload" [SubMETHOD] $ map StmtIns
            [ tempPMC <-- "subclass" $ [tempPMC, (lit $ genLit ty), fullTy]
            , tempPMC <-- "getclass" $ [fullTy]
            ] ++
            [ StmtIns $ "addattribute" .- [tempPMC, fullNum num]
            | (num, _) <- [1..] `zip` tys
            ]
        , DeclSub "__init" [SubMETHOD] $
            map StmtIns (concatMap fullArg ([1..] `zip` tys))
        ]
    ]
    where
    fullNum :: Int -> Expression
    fullNum num = lit ('$':'.':show num)
    fullTy = lit $ genLit (ty ++ "::" ++ con)
    fullArg (num, attr) = 
        [ InsNew tempPMC (bareType attr)
        , "setattribute" .- [bare "self", fullNum num, tempPMC]
        ]

bareType :: String -> ObjType
bareType t@('G':'H':'C':'.':_) = BareType $ reverse (takeWhile isAlphaNum (reverse t))
bareType t = BareType ("PIR::" ++ t)

genDec :: Dec -> PIR
genDec (DataD _ ty _ cons _) = genClass (show ty) (map conToCn cons)
genDec x = error $ show x

conToCn :: Con -> Cn
conToCn (NormalC con args) = MkCn (show con) (map (argToTy . snd) args)
conToCn x = error $ show x

argToTy :: Type -> Ty
argToTy (ConT x) = show x
argToTy (AppT ListT (ConT x)) = (show x) ++ "List"
argToTy x = error (show x)
    
test :: IO ()
test = do
    q <- runQ decls
    writeFile "pir.pir" (show $ emit $ concatMap genDec q)
    putStrLn "*** File saved as pir.pir"
    return ()

#ifndef HADDOCK
decls :: Q [Dec]
decls = [d|

    data Decl
        = DeclSub       String [SubFlag] [Stmt]
        | DeclNS        String [Decl]
        | DeclInc       FilePath
    data Stmt
        = StmtComment   String
        | StmtLine      FilePath Int
        | StmtPad       [String] [Stmt]
        | StmtIns       Ins

    data Ins
        = InsLocal      RegType String
        | InsNew        LValue ObjType
        | InsBind       LValue Expression
        | InsAssign     LValue Expression
        | InsPrim       LValue String [Expression]
        | InsFun        [Sig] Expression [Expression]
        | InsTailFun    Expression [Expression]
        | InsLabel      String
        | InsComment    String Ins
        | InsExp        Expression

    data Expression
        = ExpLV LValue
        | ExpLit Literal

    data LValue
        = VAR String
        | PMC Int
        | STR Int
        | INT Int
        | NUM Int
        | KEYED LValue Expression
        | NULL

    data Literal
        = LitStr String
        | LitInt Integer
        | LitNum Double

    data SubFlag
        = SubMAIN
        | SubLOAD
        | SubANON
        | SubMETHOD
        | SubMULTI [ObjType]

    data RegType
        = RegInt
        | RegNum
        | RegStr
        | RegPMC

    data ObjType
        = PerlScalar
        | PerlList
        | PerlHash
        | PerlInt
        | PerlPair
        | PerlRef
        | PerlEnv

    data Sig
        = MkSig [ArgFlag] Expression

    data ArgFlag
        = MkArgFlatten 
        | MkArgSlurpyArray
        | MkArgMaybeFlatten
        | MkArgOptional

    |]
#endif
