{-# LANGUAGE NoMonomorphismRestriction #-}
-- | Utility combinator functions for simplifying writing programmatic
-- generation of ECMAScript code. Recommended to use together with the
-- -XOverloadedStrings GHC extension.
module Language.ECMAScript3.Syntax.CodeGen where

import Language.ECMAScript3.Syntax
import Data.Default.Class
import Data.String

script :: Default a => [Statement a] -> JavaScript a
script = Script def

instance Default a => IsString (Id a) where
  fromString = Id def

instance Default a => IsString (Prop a) where
  fromString = PropString def

ident :: Default a => String -> Id a
ident = Id def

propId :: Default a => Id a -> Prop a
propId = PropId def

propS :: Default a => String -> Prop a
propS = PropString def

propN :: Default a => Integer -> Prop a
propN = PropNum def

instance Default a => IsString (LValue a) where
  fromString = LVar def

lvar :: Default a => String -> LValue a
lvar = LVar def

ldot :: Default a => Expression a -> String -> LValue a
ldot = LDot def

lbrack :: Default a => Expression a -> Expression a -> LValue a
lbrack = LBracket def

instance Default a => IsString (Expression a) where
  fromString = StringLit def

string :: Default a => String -> Expression a
string = StringLit def

regexp :: Default a => String -> Bool -> Bool -> Expression a
regexp = RegexpLit def

number :: Default a => Double -> Expression a
number = NumLit def

bool :: Default a => Bool -> Expression a
bool = BoolLit def

int :: Default a => Int -> Expression a
int    = IntLit def

null_ :: Default a => Expression a
null_   = NullLit def

array :: Default a => [Expression a] -> Expression a
array = ArrayLit def

object :: Default a => [(Prop a, Expression a)] -> Expression a
object = ObjectLit def

this :: Default a => Expression a
this = ThisRef def

var :: Default a => Id a -> Expression a
var = VarRef def

dot :: Default a => Expression a -> Id a -> Expression a
dot = DotRef def

brack :: Default a => Expression a -> Expression a -> Expression a
brack = BracketRef def

new :: Default a => Expression a -> [Expression a] -> Expression a
new = NewExpr def

prefix :: Default a => PrefixOp -> Expression a -> Expression a
prefix = PrefixExpr def

lnot :: Default a => Expression a -> Expression a
lnot = prefix PrefixLNot

bnot :: Default a => Expression a -> Expression a
bnot = prefix PrefixBNot

plus :: Default a => Expression a -> Expression a
plus = prefix PrefixPlus

minus :: Default a => Expression a -> Expression a
minus = prefix PrefixMinus

typeof :: Default a => Expression a -> Expression a
typeof = prefix PrefixTypeof

void :: Default a => Expression a -> Expression a
void = prefix PrefixVoid

delete :: Default a => Expression a -> Expression a
delete = prefix PrefixDelete

uassign :: Default a => UnaryAssignOp -> LValue a -> Expression a
uassign = UnaryAssignExpr def

preinc :: Default a => LValue a -> Expression a
preinc = uassign PrefixInc

predec :: Default a => LValue a -> Expression a
predec = uassign PrefixDec

postinc :: Default a => LValue a -> Expression a
postinc = uassign PostfixInc

postdec :: Default a => LValue a -> Expression a
postdec = uassign PostfixDec

infixe
  :: Default a =>
     InfixOp -> Expression a -> Expression a -> Expression a
infixe = InfixExpr def

lt :: Default a => Expression a -> Expression a -> Expression a
lt = infixe OpLT

le :: Default a => Expression a -> Expression a -> Expression a
le = infixe OpLEq

gt :: Default a => Expression a -> Expression a -> Expression a
gt = infixe OpGT

ge :: Default a => Expression a -> Expression a -> Expression a
ge = infixe OpGEq

in_ :: Default a => Expression a -> Expression a -> Expression a
in_= infixe OpIn

instanceof
  :: Default a => Expression a -> Expression a -> Expression a
instanceof = infixe OpInstanceof

eq :: Default a => Expression a -> Expression a -> Expression a
eq = infixe OpEq

neq :: Default a => Expression a -> Expression a -> Expression a
neq = infixe OpNEq

steq :: Default a => Expression a -> Expression a -> Expression a
steq = infixe OpStrictEq

stneq :: Default a => Expression a -> Expression a -> Expression a
stneq = infixe OpStrictNEq

land :: Default a => Expression a -> Expression a -> Expression a
land = infixe OpLAnd

lor :: Default a => Expression a -> Expression a -> Expression a
lor  = infixe OpLOr

mul :: Default a => Expression a -> Expression a -> Expression a
mul = infixe OpMul

div :: Default a => Expression a -> Expression a -> Expression a
div = infixe OpDiv

mod :: Default a => Expression a -> Expression a -> Expression a
mod = infixe OpMod

sub :: Default a => Expression a -> Expression a -> Expression a
sub = infixe OpSub

lshift :: Default a => Expression a -> Expression a -> Expression a
lshift = infixe OpLShift

srshift
  :: Default a => Expression a -> Expression a -> Expression a
srshift = infixe OpSpRShift

zrshift
  :: Default a => Expression a -> Expression a -> Expression a
zrshift = infixe OpZfRShift

band :: Default a => Expression a -> Expression a -> Expression a
band = infixe OpBAnd

bor :: Default a => Expression a -> Expression a -> Expression a
bor = infixe OpBOr

xor :: Default a => Expression a -> Expression a -> Expression a
xor = infixe OpBXor

add :: Default a => Expression a -> Expression a -> Expression a
add = infixe OpAdd

cond
  :: Default a =>
     Expression a -> Expression a -> Expression a -> Expression a
cond = CondExpr def

assignop :: Default a => AssignOp -> LValue a -> Expression a -> Expression a
assignop = AssignExpr def

assign
  :: Default a => LValue a -> Expression a -> Expression a
assign = assignop OpAssign

assignadd :: Default a => LValue a -> Expression a -> Expression a
assignadd = assignop OpAssignAdd

assignsub :: Default a => LValue a -> Expression a -> Expression a
assignsub = assignop OpAssignSub

assignmul :: Default a => LValue a -> Expression a -> Expression a
assignmul = assignop OpAssignMul

assigndiv :: Default a => LValue a -> Expression a -> Expression a
assigndiv = assignop OpAssignDiv

assignmod :: Default a => LValue a -> Expression a -> Expression a
assignmod = assignop OpAssignMod

assignlshift
  :: Default a => LValue a -> Expression a -> Expression a
assignlshift = assignop OpAssignLShift

assignsrshift
  :: Default a => LValue a -> Expression a -> Expression a
assignsrshift = assignop OpAssignSpRShift

assignzrshift
  :: Default a => LValue a -> Expression a -> Expression a
assignzrshift = assignop OpAssignZfRShift

assignband :: Default a => LValue a -> Expression a -> Expression a
assignband = assignop OpAssignBAnd

assignxor :: Default a => LValue a -> Expression a -> Expression a
assignxor = assignop OpAssignBXor

assignbor :: Default a => LValue a -> Expression a -> Expression a
assignbor = assignop OpAssignBOr

list :: Default a => [Expression a] -> Expression a
list = ListExpr def

call :: Default a => Expression a -> [Expression a] -> Expression a
call = CallExpr def

func
  :: Default a => Id a -> [Id a] -> [Statement a] -> Expression a
func id = FuncExpr def (Just id)

lambda :: Default a => [Id a] -> [Statement a] -> Expression a
lambda = FuncExpr def Nothing

casee :: Default a => Expression a -> [Statement a] -> CaseClause a
casee = CaseClause def

defaultc :: Default a => [Statement a] -> CaseClause a
defaultc = CaseDefault def

catch :: Default a => Id a -> Statement a -> CatchClause a
catch = CatchClause def

vardecl :: Default a => Id a -> VarDecl a
vardecl id = VarDecl def id Nothing

varinit :: Default a => Id a -> Expression a -> VarDecl a
varinit id = VarDecl def id . Just

block :: Default a => [Statement a] -> Statement a
block = BlockStmt def

empty :: Default a => Statement a
empty = EmptyStmt def

expr :: Default a => Expression a -> Statement a
expr = ExprStmt def

ifte
  :: Default a =>
     Expression a -> Statement a -> Statement a -> Statement a
ifte = IfStmt def

ift :: Default a => Expression a -> Statement a -> Statement a
ift  = IfSingleStmt def

switch
  :: Default a => Expression a -> [CaseClause a] -> Statement a
switch = SwitchStmt def

while :: Default a => Expression a -> Statement a -> Statement a
while = WhileStmt def

dowhile :: Default a => Statement a -> Expression a -> Statement a
dowhile = DoWhileStmt def

break :: Default a => Maybe (Id a) -> Statement a
break = BreakStmt def

continue :: Default a => Maybe (Id a) -> Statement a
continue = ContinueStmt def

label :: Default a => Id a -> Statement a -> Statement a
label = LabelledStmt def

forin
  :: Default a =>
     ForInInit a -> Expression a -> Statement a -> Statement a
forin = ForInStmt def

for
  :: Default a =>
     ForInit a
     -> Maybe (Expression a)
     -> Maybe (Expression a)
     -> Statement a
     -> Statement a
for = ForStmt def

try :: Default a => Statement a -> Statement a
try b = TryStmt def b Nothing Nothing

trycatch
  :: Default a =>
     Statement a -> CatchClause a -> Maybe (Statement a) -> Statement a
trycatch b c = TryStmt def b (Just c)

tryfinally
  :: Default a => Statement a -> Statement a -> Statement a
tryfinally b f = TryStmt def b Nothing (Just f)

trycatchfinally
  :: Default a =>
     Statement a -> CatchClause a -> Statement a -> Statement a
trycatchfinally b c f = TryStmt def b (Just c) (Just f)

throw :: Default a => Expression a -> Statement a
throw = ThrowStmt def

returns :: Default a => Expression a -> Statement a
returns = ReturnStmt def . Just

ret :: Default a => Statement a
ret = ReturnStmt def Nothing

with :: Default a => Expression a -> Statement a -> Statement a
with = WithStmt def

vardecls :: Default a => [VarDecl a] -> Statement a
vardecls = VarDeclStmt def

function
  :: Default a => Id a -> [Id a] -> [Statement a] -> Statement a
function = FunctionStmt def

-- | Convert an identifier to a String literal
id2string :: Id a -> Expression a
id2string (Id a s) = StringLit a s

-- | Helper function to convert LValues to expressions
lv2e :: LValue a -> Expression a
lv2e lval = case lval of
  LVar a vname -> VarRef a (Id a vname)
  LDot a obj fname -> DotRef a obj (Id a fname)
  LBracket a obj field -> BracketRef a obj field

-- | Convert an expression to an l-value. May fail with an error
e2lv :: Expression a -> LValue a
e2lv e = case e of
  VarRef a (Id _ vname) -> LVar a vname
  DotRef a obj (Id _ fname) -> LDot a obj fname
  BracketRef a obj field -> LBracket a obj field
  _ -> error "expr2LVal: Can't convert an expression to an LValue"

forInInit2lv :: ForInInit a -> LValue a
forInInit2lv i = case i of
  ForInVar (Id a s) -> LVar a s
  ForInLVal lv      -> lv