open Syntax

(* Basic value constructors *)
let int64 n = Int (Int64, n)
let int32 n = Int (Int32, n)
let int1 n = Int (Int1, n)
let double f = Double f
let var x = Var x

(* Type constructors *)
let i64 = TInt Int64
let i32 = TInt Int32
let i1 = TInt Int1
let ptr = TPtr
let void = TVoid
let tdouble = TDouble
let array ty n = TArray (ty, n)

(* Memory operations *)
let load ty addr = Load (ty, addr)
let store value addr = Store (value, addr)
let gep ty base idx = Gep (ty, base, idx)
let alloca ty = Alloca ty
let malloc ty = Malloc ty

(* Pointer conversions *)
let inttoptr e ty = Inttoptr (e, ty)
let ptrtoint e ty = Ptrtoint (e, ty)
let intcast e ty = Intcast (e, ty)
let bitcast e ty = Bitcast (e, ty)

(* Control flow *)
let if_ cond then_ else_ = If (cond, then_, else_)
let switch scrutinee cases default = Switch (scrutinee, cases, default)
let for_loop var start stop body = For (var, start, stop, body)
let seq exprs = Seq exprs

(* Primitive operations *)
let not_ e = Prim1 (Not, e)
let neg e = Prim1 (Neg, e)
let fneg e = Prim1 (FNeg, e)
let float_of_int e = Prim1 (Float_of_int, e)
let int_of_float e = Prim1 (Int_of_float, e)
let eq a b = Prim2 (Eq, a, b)
let lt a b = Prim2 (Lt, a, b)
let le a b = Prim2 (Le, a, b)
let gt a b = Prim2 (Gt, a, b)
let ge a b = Prim2 (Ge, a, b)
let and_ a b = Prim2 (And, a, b)
let or_ a b = Prim2 (Or, a, b)
let plus a b = Prim2 (Plus, a, b)
let minus a b = Prim2 (Minus, a, b)
let times a b = Prim2 (Times, a, b)
let div a b = Prim2 (Div, a, b)
let mod_ a b = Prim2 (Mod, a, b)
let shl a b = Prim2 (Shl, a, b)
let lshr a b = Prim2 (Lshr, a, b)
let ashr a b = Prim2 (Ashr, a, b)
let fplus a b = Prim2 (FPlus, a, b)
let fminus a b = Prim2 (FMinus, a, b)
let ftimes a b = Prim2 (FTimes, a, b)
let fdiv a b = Prim2 (FDiv, a, b)
let flt a b = Prim2 (FLt, a, b)
let fle a b = Prim2 (FLe, a, b)
let feq a b = Prim2 (FEq, a, b)
let fge a b = Prim2 (FGe, a, b)
let fgt a b = Prim2 (FGt, a, b)

(* Function calls *)
let apply fn args ret_ty = Apply (fn, args, ret_ty)
let apply_ext name arg_tys ret_ty args = ApplyExt (name, arg_tys, ret_ty, args)
let func name = Func name

(* Let binding *)
let let_ name expr body = Let (name, expr, body)

(* Let binding with infix operator *)
let ( let@ ) name expr body = Let (name, expr, body)
