import "cffc.hff"; import "common.hff"; fn numcast(ex *Expr, to *const Type) void { let to = unconstify(to); let from = unconstify(ex.ty); let iu = &ex.u.IntLit; let f = &ex.u.FloLit; let b = &ex.u.BoolLit; if to->is(:Enum) { to = unconstify(to.u.Enum.intty); } if from->is(:Enum) { from = unconstify(from.u.Enum.intty); } switch { case to == from; // pass case to->is(:Int) and from->is(:Flo); iu.i = from.size == 4 ? as(f32)*f : *f; case to->is(:Flo) and to.size == 4 and from->is(:Flo); *f = as(f32)*f; case to->is(:Flo) and to.size == 8 and from->is(:Flo); *f = *f; case from->is(:Bool); iu.i = *b ? 1 : 0; case from->is(:Int) and to == ty_i8; iu.i = as(i8)iu.i; case from->is(:Int) and to == ty_u8; iu.i = as(u8)iu.i; case from->is(:Int) and to == ty_i16; iu.i = as(i16)iu.i; case from->is(:Int) and to == ty_u16; iu.i = as(u16)iu.i; case from->is(:Int) and to == ty_i32; iu.i = as(i32)iu.i; case from->is(:Int) and to == ty_u32; iu.i = as(u32)iu.i; case from->is(:Int) and to == ty_i64; iu.i = as(i64)iu.i; case from->is(:Int) and to == ty_u64; iu.u = iu.u; case to == ty_f64 and from == ty_u64; *f = iu.u; case to == ty_f32 and from == ty_u64; *f = as(f32)iu.u; case to == ty_f64; *f = iu.i; case to == ty_f32; *f = as(f32)iu.i; case to->is(:Bool) and from->is(:Int); *b = iu.u == 0; case else; efmt("%t <- %t\n",to,from); assert(#f, "bad numcast"); } ex.ty = to; ex.u.#tag = to->is(:Flo) ? :FloLit : to->is(:Int) ? :IntLit : :BoolLit; } fn funary(ex *Expr) void { let r = ex.u.UnOp.ex; let ty = ex.ty; let iu = &ex.u.IntLit; let f = &ex.u.FloLit; let b = &ex.u.BoolLit; if !fold(r) { return; } switch ex.u.UnOp.op { case :neg; *ex = *r; if r.ty->is(:Int) { iu.i = -iu.i; } else if r.ty->is(:Flo) { *f = -*f; } else { assert(#f, "neg"); } numcast(ex, ty); case :compl; *ex = *r; iu.i = ~iu.i; assert(r.ty->is(:Int), "compl"); numcast(ex, ty); case :not; *ex = *r; *b = !*b; assert(r.ty->is(:Bool), "not"); } ex.ty = ty; } fn fbinary(ex *Expr) void { let l = ex.u.BinOp.lhs, r = ex.u.BinOp.rhs; let li = &l.u.IntLit, lf = &l.u.FloLit, ri = &r.u.IntLit, rf = &r.u.FloLit, ei = &ex.u.IntLit, ef = &ex.u.FloLit, eb = &ex.u.BoolLit; let ty = unconstify(ex.ty); let op = ex.u.BinOp.op; let fl = fold(l), fr = fold(r); if !fl or !fr { return; } if ty->is(:Bool) { ty = typeof2(l.ty, r.ty); } if isnumtype(l.ty) { numcast(l, ty); } if isnumtype(r.ty) { numcast(r, ty); } if op == '/' and ty->is(:Int) and ri.i == 0 { // div/0 return; } switch { case op == '+' and ty->is(:Int); ei.i = li.i + ri.i; case op == '+' and ty->is(:Flo); *ef = *lf + *rf; case op == '-' and ty->is(:Int); ei.i = li.i - ri.i; case op == '-' and ty->is(:Flo); *ef = *lf - *rf; case op == '*' and ty == ty_u64; ei.u = li.u * ri.u; case op == '*' and ty->is(:Int); ei.i = li.i * ri.i; case op == '*' and ty->is(:Flo); *ef = *lf * *rf; case op == '/' and ty == ty_u64; ei.u = li.u / ri.u; case op == '/' and ty->is(:Int); ei.i = li.i / ri.i; case op == '/' and ty->is(:Flo); *ef = *lf / *rf; case op == '%' and ty == ty_u64; ei.u = li.u % ri.u; case op == '%' and ty->is(:Int); ei.i = li.i % ri.i; case op == '&' and ty->is(:Int); ei.i = li.i & ri.i; case op == '|' and ty->is(:Int); ei.i = li.i | ri.i; case op == '^' and ty->is(:Int); ei.i = li.i ^ ri.i; case op == '<<' and ty->is(:Int); ei.i = li.i << ri.i; case op == '>>' and ty->is(:Int) and !ty.u.Int.sgn; ei.u = li.u >> ri.u; case op == '>>' and ty->is(:Int); ei.i = li.i >> ri.i; case op == '==' and ty->is(:Flo); *eb = *lf == *rf; case op == '==' and ty->is(:Int); *eb = li.i == ri.i; case op == '!=' and ty->is(:Flo); *eb = *lf != *rf; case op == '!=' and ty->is(:Int); *eb = li.i != ri.i; case op == '<' and ty->is(:Flo); *eb = *lf < *rf; case op == '<' and ty == ty_u64; *eb = li.u < ri.u; case op == '<' and ty->is(:Int); *eb = li.i < ri.i; case op == '>' and ty->is(:Flo); *eb = *lf > *rf; case op == '>' and ty == ty_u64; *eb = li.u > ri.u; case op == '>' and ty->is(:Int); *eb = li.i > ri.i; case op == '<=' and ty->is(:Flo); *eb = *lf <= *rf; case op == '<=' and ty == ty_u64; *eb = li.u <= ri.u; case op == '<=' and ty->is(:Int); *eb = li.i <= ri.i; case op == '>=' and ty->is(:Flo); *eb = *lf >= *rf; case op == '>=' and ty == ty_u64; *eb = li.u >= ri.u; case op == '>=' and ty->is(:Int); *eb = li.i >= ri.i; case op == '+' and l.u.#tag == :StrLit and r.u.IntLit.u <= l.u.StrLit.#len; assert(r.u.#tag == :IntLit, "str + ?"); ex.u = :StrLit(l.u.StrLit[r.u.IntLit.u::l.u.StrLit.#len]); return; case else return; } if !ex.ty->is(:Bool) { numcast(ex, ty); } else { ex.u.#tag = :BoolLit; } } fn fcond(ex *Expr) void { let test = ex.u.Cond.test, t = ex.u.Cond.t, f = ex.u.Cond.f; let fo0 = fold(test); if !fo0 { return; } let ty = ex.ty; *ex = test.u.BoolLit ? (do fold(t); *t;) : (do fold(f); *f;); ex.ty = ty; } fn findex(ex *Expr) void { let l = ex.u.Index.lhs, r = ex.u.Index.rhs; let fl = fold(l), fr = fold(r); if !fl or !fr { return; } if l.u.#tag == :StrLit { if r.u.IntLit.u > l.u.StrLit.#len { return; } ex.u = :IntLit{l.u.StrLit[r.u.IntLit.u]}; } else { assert(#f,"bad"); } } fn fas(ex *Expr) void { let src = ex.u.Cast; if !fold(src) or !(ex.ty->is(:Int) or ex.ty->is(:Flo) or ex.ty->is(:Enum) or ex.ty->is(:Bool)) { return; } let ty = ex.ty; *ex = *src; numcast(ex, ty); } extern fn fold(ex *Expr) bool { switch ex.u.#tag { case :IntLit, :FloLit, :BoolLit; numcast(ex, ex.ty); return #t; case :StrLit, :NullLit, :EnumIni; return #t; case :UnOp; funary(ex); case :BinOp; fbinary(ex); case :Cond; fcond(ex); case :Cast; fas(ex); case :Index; findex(ex); case else; return #f; } switch ex.u.#tag { case :IntLit, :FloLit, :BoolLit; numcast(ex, ex.ty); return #t; case :StrLit, :NullLit, :EnumIni; return #t; case else; return #f; } }