1
0
mirror of https://github.com/golang/go synced 2024-11-26 20:11:26 -07:00
go/usr/austin/eval/expr.go
Austin Clements 8f694f6661 Flatten the Frame tree. Now each function call produces a
single frame and non-overlapping variables reuse frame slots.
As a result, entering and exiting blocks no longer requires
code execution, which means jumps across block boundaries
should be doable now.  Frame slot initialization happens at
definition time now, instead of at frame creation time.  As an
added bonus, Scope's are now exclusively compile-time objects
and we no longer need to specially track the function
activation frame for access to out vars.

R=rsc
APPROVED=rsc
DELTA=313  (102 added, 90 deleted, 121 changed)
OCL=32416
CL=32420
2009-07-29 11:57:46 -07:00

2116 lines
58 KiB
Go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package eval
import (
"bignum";
"eval";
"go/ast";
"go/scanner";
"go/token";
"log";
"os";
"strconv";
"strings";
)
// An exprCompiler compiles a single node in an expression. It stores
// the whole expression's context plus information specific to this node.
// After compilation, it stores the type of the expression and its
// evaluator function.
type exprCompiler struct {
*exprContext;
pos token.Position;
t Type;
// Evaluate this node as the given type.
evalBool func(f *Frame) bool;
evalUint func(f *Frame) uint64;
evalInt func(f *Frame) int64;
// TODO(austin) evalIdealInt and evalIdealFloat shouldn't be
// functions at all.
evalIdealInt func() *bignum.Integer;
evalFloat func(f *Frame) float64;
evalIdealFloat func() *bignum.Rational;
evalString func(f *Frame) string;
evalArray func(f *Frame) ArrayValue;
evalPtr func(f *Frame) Value;
evalFunc func(f *Frame) Func;
evalMulti func(f *Frame) []Value;
// Evaluate to the "address of" this value; that is, the
// settable Value object. nil for expressions whose address
// cannot be taken.
evalAddr func(f *Frame) Value;
// Execute this expression as a statement. Only expressions
// that are valid expression statements should set this.
exec func(f *Frame);
// A short string describing this expression for error
// messages. Only necessary if t != nil.
desc string;
}
func newExprCompiler(c *exprContext, pos token.Position) *exprCompiler {
return &exprCompiler{
exprContext: c,
pos: pos,
desc: "<missing description>"
};
}
// Operator generators
// TODO(austin) Remove these forward declarations
func (a *exprCompiler) genConstant(v Value)
func (a *exprCompiler) genIdentOp(level int, index int)
func (a *exprCompiler) genIndexArray(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genFuncCall(call func(f *Frame) []Value)
func (a *exprCompiler) genStarOp(v *exprCompiler)
func (a *exprCompiler) genUnaryOpNeg(v *exprCompiler)
func (a *exprCompiler) genUnaryOpNot(v *exprCompiler)
func (a *exprCompiler) genUnaryOpXor(v *exprCompiler)
func (a *exprCompiler) genBinOpAdd(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpSub(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpMul(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpQuo(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpRem(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpAnd(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpOr(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpXor(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpAndNot(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpShl(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpShr(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpLss(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpGtr(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpLeq(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpGeq(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpEql(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpNeq(l *exprCompiler, r *exprCompiler)
func genAssign(lt Type, r *exprCompiler) (func(lv Value, f *Frame))
func (a *exprCompiler) copy() *exprCompiler {
ec := newExprCompiler(a.exprContext, a.pos);
ec.desc = a.desc;
return ec;
}
func (a *exprCompiler) copyVisit(x ast.Expr) *exprCompiler {
ec := newExprCompiler(a.exprContext, x.Pos());
x.Visit(ec);
return ec;
}
func (a *exprCompiler) diag(format string, args ...) {
a.diagAt(&a.pos, format, args);
}
func (a *exprCompiler) diagOpType(op token.Token, vt Type) {
a.diag("illegal operand type for '%v' operator\n\t%v", op, vt);
}
func (a *exprCompiler) diagOpTypes(op token.Token, lt Type, rt Type) {
a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt);
}
/*
* "As" functions. These retrieve evaluator functions from an
* exprCompiler, panicking if the requested evaluator is nil.
*/
func (a *exprCompiler) asBool() (func(f *Frame) bool) {
if a.evalBool == nil {
log.Crashf("tried to get %v node as boolType", a.t);
}
return a.evalBool;
}
func (a *exprCompiler) asUint() (func(f *Frame) uint64) {
if a.evalUint == nil {
log.Crashf("tried to get %v node as uintType", a.t);
}
return a.evalUint;
}
func (a *exprCompiler) asInt() (func(f *Frame) int64) {
if a.evalInt == nil {
log.Crashf("tried to get %v node as intType", a.t);
}
return a.evalInt;
}
func (a *exprCompiler) asIdealInt() (func() *bignum.Integer) {
if a.evalIdealInt == nil {
log.Crashf("tried to get %v node as idealIntType", a.t);
}
return a.evalIdealInt;
}
func (a *exprCompiler) asFloat() (func(f *Frame) float64) {
if a.evalFloat == nil {
log.Crashf("tried to get %v node as floatType", a.t);
}
return a.evalFloat;
}
func (a *exprCompiler) asIdealFloat() (func() *bignum.Rational) {
if a.evalIdealFloat == nil {
log.Crashf("tried to get %v node as idealFloatType", a.t);
}
return a.evalIdealFloat;
}
func (a *exprCompiler) asString() (func(f *Frame) string) {
if a.evalString == nil {
log.Crashf("tried to get %v node as stringType", a.t);
}
return a.evalString;
}
func (a *exprCompiler) asArray() (func(f *Frame) ArrayValue) {
if a.evalArray == nil {
log.Crashf("tried to get %v node as ArrayType", a.t);
}
return a.evalArray;
}
func (a *exprCompiler) asPtr() (func(f *Frame) Value) {
if a.evalPtr == nil {
log.Crashf("tried to get %v node as PtrType", a.t);
}
return a.evalPtr;
}
func (a *exprCompiler) asFunc() (func(f *Frame) Func) {
if a.evalFunc == nil {
log.Crashf("tried to get %v node as FuncType", a.t);
}
return a.evalFunc;
}
func (a *exprCompiler) asMulti() (func(f *Frame) []Value) {
if a.evalMulti == nil {
log.Crashf("tried to get %v node as MultiType", a.t);
}
return a.evalMulti;
}
/*
* Common expression manipulations
*/
// a.convertTo(t) converts the value of the analyzed expression a,
// which must be a constant, ideal number, to a new analyzed
// expression with a constant value of type t.
//
// TODO(austin) Rename to resolveIdeal or something?
func (a *exprCompiler) convertTo(t Type) *exprCompiler {
if !a.t.isIdeal() {
log.Crashf("attempted to convert from %v, expected ideal", a.t);
}
var rat *bignum.Rational;
// XXX(Spec) The spec says "It is erroneous".
//
// It is an error to assign a value with a non-zero fractional
// part to an integer, or if the assignment would overflow or
// underflow, or in general if the value cannot be represented
// by the type of the variable.
switch a.t {
case IdealFloatType:
rat = a.asIdealFloat()();
if t.isInteger() && !rat.IsInt() {
a.diag("constant %v truncated to integer", ratToString(rat));
return nil;
}
case IdealIntType:
i := a.asIdealInt()();
rat = bignum.MakeRat(i, bignum.Nat(1));
default:
log.Crashf("unexpected ideal type %v", a.t);
}
// Check bounds
if t, ok := t.rep().(BoundedType); ok {
if rat.Cmp(t.minVal()) < 0 {
a.diag("constant %v underflows %v", ratToString(rat), t);
return nil;
}
if rat.Cmp(t.maxVal()) > 0 {
a.diag("constant %v overflows %v", ratToString(rat), t);
return nil;
}
}
// Convert rat to type t.
res := a.copy();
res.t = t;
switch t := t.rep().(type) {
case *uintType:
n, d := rat.Value();
f := n.Quo(bignum.MakeInt(false, d));
v := f.Abs().Value();
res.evalUint = func(*Frame) uint64 { return v };
case *intType:
n, d := rat.Value();
f := n.Quo(bignum.MakeInt(false, d));
v := f.Value();
res.evalInt = func(*Frame) int64 { return v };
case *idealIntType:
n, d := rat.Value();
f := n.Quo(bignum.MakeInt(false, d));
res.evalIdealInt = func() *bignum.Integer { return f };
case *floatType:
n, d := rat.Value();
v := float64(n.Value())/float64(d.Value());
res.evalFloat = func(*Frame) float64 { return v };
case *idealFloatType:
res.evalIdealFloat = func() *bignum.Rational { return rat };
default:
log.Crashf("cannot convert to type %T", t);
}
return res;
}
/*
* Assignments
*/
// An assignCompiler compiles assignment operations. Anything other
// than short declarations should use the compileAssign wrapper.
//
// There are three valid types of assignment:
// 1) T = T
// Assigning a single expression with single-valued type to a
// single-valued type.
// 2) MT = T, T, ...
// Assigning multiple expressions with single-valued types to a
// multi-valued type.
// 3) MT = MT
// Assigning a single expression with multi-valued type to a
// multi-valued type.
type assignCompiler struct {
*compiler;
pos token.Position;
// The RHS expressions. This may include nil's for
// expressions that failed to compile.
rs []*exprCompiler;
// The (possibly unary) MultiType of the RHS.
rmt *MultiType;
// Whether this is an unpack assignment (case 3).
isUnpack bool;
// The operation name to use in error messages, such as
// "assignment" or "function call".
errOp string;
// The name to use for positions in error messages, such as
// "argument".
errPosName string;
}
// Type check the RHS of an assignment, returning a new assignCompiler
// and indicating if the type check succeeded. This always returns an
// assignCompiler with rmt set, but if type checking fails, slots in
// the MultiType may be nil. If rs contains nil's, type checking will
// fail and these expressions given a nil type.
func (a *compiler) checkAssign(pos token.Position, rs []*exprCompiler, errOp, errPosName string) (*assignCompiler, bool) {
c := &assignCompiler{
compiler: a,
pos: pos,
rs: rs,
errOp: errOp,
errPosName: errPosName,
};
// Is this an unpack?
if len(rs) == 1 && rs[0] != nil {
if rmt, isUnpack := rs[0].t.(*MultiType); isUnpack {
c.rmt = rmt;
c.isUnpack = true;
return c, true;
}
}
// Create MultiType for RHS and check that all RHS expressions
// are single-valued.
rts := make([]Type, len(rs));
ok := true;
for i, r := range rs {
if r == nil {
ok = false;
continue;
}
if _, isMT := r.t.(*MultiType); isMT {
r.diag("multi-valued expression not allowed in %s", errOp);
ok = false;
continue;
}
rts[i] = r.t;
}
c.rmt = NewMultiType(rts);
return c, ok;
}
// compile type checks and compiles an assignment operation, returning
// a function that expects an l-value and the frame in which to
// evaluate the RHS expressions. The l-value must have exactly the
// type given by lt. Returns nil if type checking fails.
func (a *assignCompiler) compile(lt Type) (func(lv Value, f *Frame)) {
lmt, isMT := lt.(*MultiType);
rmt, isUnpack := a.rmt, a.isUnpack;
// Create unary MultiType for single LHS
if !isMT {
lmt = NewMultiType([]Type{lt});
}
// Check that the assignment count matches
lcount := len(lmt.Elems);
rcount := len(rmt.Elems);
if lcount != rcount {
msg := "not enough";
pos := a.pos;
if rcount > lcount {
msg = "too many";
if lcount > 0 {
pos = a.rs[lcount-1].pos;
}
}
a.diagAt(&pos, "%s %ss for %s\n\t%s\n\t%s", msg, a.errPosName, a.errOp, lt, rmt);
return nil;
}
bad := false;
// TODO(austin) Deal with assignment special cases. This is
// tricky in the unpack case, since some of the conversions
// can apply to single types within the multi-type.
// Values of any type may always be assigned to variables of
// compatible static type.
for i, lt := range lmt.Elems {
// Check each type individually so we can produce a
// better error message.
rt := rmt.Elems[i];
// When [an ideal is] (used in an expression) assigned
// to a variable or typed constant, the destination
// must be able to represent the assigned value.
if rt.isIdeal() {
if isUnpack {
log.Crashf("Right side of unpack contains ideal: %s", rmt);
}
a.rs[i] = a.rs[i].convertTo(lmt.Elems[i]);
if a.rs[i] == nil {
bad = true;
continue;
}
rt = a.rs[i].t;
}
if lt.literal() != rt.literal() {
if len(a.rs) == 1 {
a.rs[0].diag("illegal operand types for %s\n\t%v\n\t%v", a.errOp, lt, rt);
} else {
a.rs[i].diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", a.errPosName, i+1, a.errOp, lt, rt);
}
bad = true;
}
}
if bad {
return nil;
}
// Compile
switch {
case !isMT:
// Case 1
return genAssign(lt, a.rs[0]);
case !isUnpack:
// Case 2
as := make([]func(lv Value, f *Frame), len(a.rs));
for i, r := range a.rs {
as[i] = genAssign(lmt.Elems[i], r);
}
return func(lv Value, f *Frame) {
lmv := lv.(multiV);
for i, a := range as {
a(lmv[i], f);
}
};
default:
// Case 3
rf := a.rs[0].asMulti();
return func(lv Value, f *Frame) {
lv.Assign(multiV(rf(f)));
};
}
panic();
}
// compileAssign compiles an assignment operation without the full
// generality of an assignCompiler. See assignCompiler for a
// description of the arguments.
func (a *compiler) compileAssign(pos token.Position, lt Type, rs []*exprCompiler, errOp, errPosName string) (func(lv Value, f *Frame)) {
ac, ok := a.checkAssign(pos, rs, errOp, errPosName);
if !ok {
return nil;
}
return ac.compile(lt);
}
/*
* Expression visitors
*/
func (a *exprCompiler) DoBadExpr(x *ast.BadExpr) {
// Do nothing. Already reported by parser.
}
func (a *exprCompiler) DoIdent(x *ast.Ident) {
level, def := a.block.Lookup(x.Value);
if def == nil {
a.diag("%s: undefined", x.Value);
return;
}
switch def := def.(type) {
case *Constant:
a.t = def.Type;
a.genConstant(def.Value);
a.desc = "constant";
case *Variable:
if a.constant {
a.diag("variable %s used in constant expression", x.Value);
return;
}
if def.Type == nil {
// Placeholder definition from an earlier error
return;
}
a.t = def.Type;
defidx := def.Index;
a.genIdentOp(level, defidx);
a.desc = "variable";
case Type:
a.diag("type %v used as expression", x.Value);
default:
log.Crashf("name %s has unknown type %T", x.Value, def);
}
}
func (a *exprCompiler) doIdealInt(i *bignum.Integer) {
a.t = IdealIntType;
a.evalIdealInt = func() *bignum.Integer { return i };
}
func (a *exprCompiler) DoIntLit(x *ast.IntLit) {
i, _, _2 := bignum.IntFromString(string(x.Value), 0);
a.doIdealInt(i);
a.desc = "integer literal";
}
func (a *exprCompiler) DoCharLit(x *ast.CharLit) {
if x.Value[0] != '\'' {
log.Crashf("malformed character literal %s at %v passed parser", x.Value, x.Pos());
}
v, mb, tail, err := strconv.UnquoteChar(string(x.Value[1:len(x.Value)]), '\'');
if err != nil || tail != "'" {
log.Crashf("malformed character literal %s at %v passed parser", x.Value, x.Pos());
}
a.doIdealInt(bignum.Int(int64(v)));
a.desc = "character literal";
}
func (a *exprCompiler) DoFloatLit(x *ast.FloatLit) {
f, _, n := bignum.RatFromString(string(x.Value), 0);
if n != len(x.Value) {
log.Crashf("malformed float literal %s at %v passed parser", x.Value, x.Pos());
}
a.t = IdealFloatType;
a.evalIdealFloat = func() *bignum.Rational { return f };
a.desc = "float literal";
}
func (a *exprCompiler) doString(s string) {
a.t = StringType;
a.evalString = func(*Frame) string { return s };
}
func (a *exprCompiler) DoStringLit(x *ast.StringLit) {
s, err := strconv.Unquote(string(x.Value));
if err != nil {
a.diag("illegal string literal, %v", err);
return;
}
a.doString(s);
a.desc = "string literal";
}
func (a *exprCompiler) DoStringList(x *ast.StringList) {
ss := make([]string, len(x.Strings));
for i := 0; i < len(x.Strings); i++ {
s, err := strconv.Unquote(string(x.Strings[i].Value));
if err != nil {
a.diag("illegal string literal, %v", err);
return;
}
ss[i] = s;
}
a.doString(strings.Join(ss, ""));
a.desc = "string literal";
}
func (a *exprCompiler) DoFuncLit(x *ast.FuncLit) {
// TODO(austin) Closures capture their entire defining frame
// instead of just the variables they use.
decl := a.compileFuncType(a.block, x.Type);
if decl == nil {
// TODO(austin) Try compiling the body, perhaps with
// dummy definitions for the arguments
return;
}
evalFunc := a.compileFunc(a.block, decl, x.Body);
if evalFunc == nil {
return;
}
if a.constant {
a.diag("function literal used in constant expression");
return;
}
a.t = decl.Type;
a.evalFunc = evalFunc;
}
func (a *exprCompiler) DoCompositeLit(x *ast.CompositeLit) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoParenExpr(x *ast.ParenExpr) {
x.X.Visit(a);
}
func (a *exprCompiler) DoSelectorExpr(x *ast.SelectorExpr) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoIndexExpr(x *ast.IndexExpr) {
l, r := a.copyVisit(x.X), a.copyVisit(x.Index);
if l.t == nil || r.t == nil {
return;
}
// Type check object
if lt, ok := l.t.rep().(*PtrType); ok {
if et, ok := lt.Elem.rep().(*ArrayType); ok {
// Automatic dereference
nl := l.copy();
nl.t = et;
nl.genStarOp(l);
l = nl;
}
}
var at Type;
intIndex := false;
var maxIndex int64 = -1;
switch lt := l.t.rep().(type) {
case *ArrayType:
at = lt.Elem;
intIndex = true;
maxIndex = lt.Len;
// TODO(austin) Uncomment when there is a SliceType
// case *SliceType:
// a.t = lt.Elem;
// intIndex = true;
case *stringType:
at = Uint8Type;
intIndex = true;
// TODO(austin) Uncomment when there is a MapType
// case *MapType:
// log.Crash("Index into map not implemented");
default:
a.diag("cannot index into %v", l.t);
return;
}
// Type check index and convert to int if necessary
if intIndex {
// XXX(Spec) It's unclear if ideal floats with no
// fractional part are allowed here. 6g allows it. I
// believe that's wrong.
switch _ := r.t.rep().(type) {
case *idealIntType:
val := r.asIdealInt()();
if val.IsNeg() || (maxIndex != -1 && val.Cmp(bignum.Int(maxIndex)) >= 0) {
a.diag("array index out of bounds");
return;
}
r = r.convertTo(IntType);
if r == nil {
return;
}
case *uintType:
// Convert to int
nr := r.copy();
nr.t = IntType;
rf := r.asUint();
nr.evalInt = func(f *Frame) int64 {
return int64(rf(f));
};
r = nr;
case *intType:
// Good as is
default:
a.diag("illegal operand type for index\n\t%v", r.t);
return;
}
}
a.t = at;
// Compile
switch lt := l.t.rep().(type) {
case *ArrayType:
a.t = lt.Elem;
// TODO(austin) Bounds check
a.genIndexArray(l, r);
lf := l.asArray();
rf := r.asInt();
a.evalAddr = func(f *Frame) Value {
return lf(f).Elem(rf(f));
};
case *stringType:
// TODO(austin) Bounds check
lf := l.asString();
rf := r.asInt();
// TODO(austin) This pulls over the whole string in a
// remote setting, instead of just the one character.
a.evalUint = func(f *Frame) uint64 {
return uint64(lf(f)[rf(f)]);
}
default:
log.Crashf("Compilation of index into %T not implemented", l.t);
}
}
func (a *exprCompiler) DoTypeAssertExpr(x *ast.TypeAssertExpr) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoCallExpr(x *ast.CallExpr) {
// TODO(austin) Type conversions look like calls, but will
// fail in DoIdent right now.
//
// TODO(austin) Magic built-in functions
//
// TODO(austin) Variadic functions.
// Compile children
bad := false;
l := a.copyVisit(x.Fun);
if l.t == nil {
bad = true;
}
as := make([]*exprCompiler, len(x.Args));
for i := 0; i < len(x.Args); i++ {
as[i] = a.copyVisit(x.Args[i]);
if as[i].t == nil {
bad = true;
}
}
if bad {
return;
}
// Type check
if a.constant {
a.diag("function call in constant context");
return;
}
// XXX(Spec) Calling a named function type is okay. I really
// think there needs to be a general discussion of named
// types. A named type creates a new, distinct type, but the
// type of that type is still whatever it's defined to. Thus,
// in "type Foo int", Foo is still an integer type and in
// "type Foo func()", Foo is a function type.
lt, ok := l.t.rep().(*FuncType);
if !ok {
a.diag("cannot call non-function type %v", l.t);
return;
}
// The arguments must be single-valued expressions assignment
// compatible with the parameters of F.
//
// XXX(Spec) The spec is wrong. It can also be a single
// multi-valued expression.
nin := len(lt.In);
assign := a.compileAssign(x.Pos(), NewMultiType(lt.In), as, "function call", "argument");
if assign == nil {
return;
}
nout := len(lt.Out);
switch nout {
case 0:
a.t = EmptyType;
case 1:
a.t = lt.Out[0];
default:
a.t = NewMultiType(lt.Out);
}
// Gather argument and out types to initialize frame variables
vts := make([]Type, nin + nout);
for i, t := range lt.In {
vts[i] = t;
}
for i, t := range lt.Out {
vts[i+nin] = t;
}
// Compile
lf := l.asFunc();
call := func(f *Frame) []Value {
fun := lf(f);
fr := fun.NewFrame();
for i, t := range vts {
fr.Vars[i] = t.Zero();
}
assign(multiV(fr.Vars[0:nin]), f);
fun.Call(fr);
return fr.Vars[nin:nin+nout];
};
a.genFuncCall(call);
}
func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) {
v := a.copyVisit(x.X);
if v.t == nil {
return;
}
switch vt := v.t.rep().(type) {
case *PtrType:
a.t = vt.Elem;
a.genStarOp(v);
a.desc = "indirect expression";
default:
a.diagOpType(token.MUL, v.t);
}
}
func (a *exprCompiler) genUnaryAddrOf(v *exprCompiler) {
vf := v.evalAddr;
a.evalPtr = func(f *Frame) Value { return vf(f) };
}
var unaryOpDescs = make(map[token.Token] string)
func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) {
v := a.copyVisit(x.X);
if v.t == nil {
return;
}
// Type check
switch x.Op {
case token.ADD, token.SUB:
if !v.t.isInteger() && !v.t.isFloat() {
a.diagOpType(x.Op, v.t);
return;
}
a.t = v.t;
case token.NOT:
if !v.t.isBoolean() {
a.diagOpType(x.Op, v.t);
return;
}
a.t = BoolType;
case token.XOR:
if !v.t.isInteger() {
a.diagOpType(x.Op, v.t);
return;
}
a.t = v.t;
case token.AND:
// The unary prefix address-of operator & generates
// the address of its operand, which must be a
// variable, pointer indirection, field selector, or
// array or slice indexing operation.
if v.evalAddr == nil {
a.diag("cannot take the address of %s", v.desc);
return;
}
// TODO(austin) Implement "It is illegal to take the
// address of a function result variable" once I have
// function result variables.
a.t = NewPtrType(v.t);
case token.ARROW:
log.Crashf("Unary op %v not implemented", x.Op);
default:
log.Crashf("unknown unary operator %v", x.Op);
}
var ok bool;
a.desc, ok = unaryOpDescs[x.Op];
if !ok {
a.desc = "unary " + x.Op.String() + " expression";
unaryOpDescs[x.Op] = a.desc;
}
// Compile
switch x.Op {
case token.ADD:
// Just compile it out
*a = *v;
case token.SUB:
a.genUnaryOpNeg(v);
case token.NOT:
a.genUnaryOpNot(v);
case token.XOR:
a.genUnaryOpXor(v);
case token.AND:
a.genUnaryAddrOf(v);
default:
log.Crashf("Compilation of unary op %v not implemented", x.Op);
}
}
var binOpDescs = make(map[token.Token] string)
func (a *exprCompiler) doBinaryExpr(op token.Token, l, r *exprCompiler) {
// Save the original types of l.t and r.t for error messages.
origlt := l.t;
origrt := r.t;
// XXX(Spec) What is the exact definition of a "named type"?
// XXX(Spec) Arithmetic operators: "Integer types" apparently
// means all types compatible with basic integer types, though
// this is never explained. Likewise for float types, etc.
// This relates to the missing explanation of named types.
// XXX(Spec) Operators: "If both operands are ideal numbers,
// the conversion is to ideal floats if one of the operands is
// an ideal float (relevant for / and %)." How is that
// relevant only for / and %? If I add an ideal int and an
// ideal float, I get an ideal float.
if op != token.SHL && op != token.SHR {
// Except in shift expressions, if one operand has
// numeric type and the other operand is an ideal
// number, the ideal number is converted to match the
// type of the other operand.
if (l.t.isInteger() || l.t.isFloat()) && !l.t.isIdeal() && r.t.isIdeal() {
r = r.convertTo(l.t);
} else if (r.t.isInteger() || r.t.isFloat()) && !r.t.isIdeal() && l.t.isIdeal() {
l = l.convertTo(r.t);
}
if l == nil || r == nil {
return;
}
// Except in shift expressions, if both operands are
// ideal numbers and one is an ideal float, the other
// is converted to ideal float.
if l.t.isIdeal() && r.t.isIdeal() {
if l.t.isInteger() && r.t.isFloat() {
l = l.convertTo(r.t);
} else if l.t.isFloat() && r.t.isInteger() {
r = r.convertTo(l.t);
}
if l == nil || r == nil {
return;
}
}
}
// XXX(Spec) "The operand types in binary operations must be
// compatible" should say the types must be *identical*.
// Useful type predicates
same := func() bool {
return l.t == r.t;
};
integers := func() bool {
return l.t.isInteger() && r.t.isInteger();
};
floats := func() bool {
return l.t.isFloat() && r.t.isFloat();
};
strings := func() bool {
// TODO(austin) Deal with named types
return l.t == StringType && r.t == StringType;
};
booleans := func() bool {
return l.t.isBoolean() && r.t.isBoolean();
};
// Type check
switch op {
case token.ADD:
if !same() || (!integers() && !floats() && !strings()) {
a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = l.t;
case token.SUB, token.MUL, token.QUO:
if !same() || (!integers() && !floats()) {
a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = l.t;
case token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
if !same() || !integers() {
a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = l.t;
case token.SHL, token.SHR:
// XXX(Spec) Is it okay for the right operand to be an
// ideal float with no fractional part? "The right
// operand in a shift operation must be always be of
// unsigned integer type or an ideal number that can
// be safely converted into an unsigned integer type
// (§Arithmetic operators)" suggests so and 6g agrees.
if !l.t.isInteger() || !(r.t.isInteger() || r.t.isIdeal()) {
a.diagOpTypes(op, origlt, origrt);
return;
}
// The right operand in a shift operation must be
// always be of unsigned integer type or an ideal
// number that can be safely converted into an
// unsigned integer type.
if r.t.isIdeal() {
r2 := r.convertTo(UintType);
if r2 == nil {
return;
}
// If the left operand is not ideal, convert
// the right to not ideal.
if !l.t.isIdeal() {
r = r2;
}
// If both are ideal, but the right side isn't
// an ideal int, convert it to simplify things.
if l.t.isIdeal() && !r.t.isInteger() {
r = r.convertTo(IdealIntType);
if r == nil {
log.Crashf("conversion to uintType succeeded, but conversion to idealIntType failed");
}
}
} else if _, ok := r.t.rep().(*uintType); !ok {
a.diag("right operand of shift must be unsigned");
return;
}
if l.t.isIdeal() && !r.t.isIdeal() {
// XXX(Spec) What is the meaning of "ideal >>
// non-ideal"? Russ says the ideal should be
// converted to an int. 6g propagates the
// type down from assignments as a hint.
l = l.convertTo(IntType);
if l == nil {
return;
}
}
// At this point, we should have one of three cases:
// 1) uint SHIFT uint
// 2) int SHIFT uint
// 3) ideal int SHIFT ideal int
a.t = l.t;
case token.LOR, token.LAND:
if !booleans() {
return;
}
// XXX(Spec) There's no mention of *which* boolean
// type the logical operators return. From poking at
// 6g, it appears to be the named boolean type, NOT
// the type of the left operand, and NOT an unnamed
// boolean type.
a.t = BoolType;
case token.ARROW:
// The operands in channel sends differ in type: one
// is always a channel and the other is a variable or
// value of the channel's element type.
log.Crash("Binary op <- not implemented");
a.t = BoolType;
case token.LSS, token.GTR, token.LEQ, token.GEQ:
// XXX(Spec) It's really unclear what types which
// comparison operators apply to. I feel like the
// text is trying to paint a Venn diagram for me,
// which it's really pretty simple: <, <=, >, >= apply
// only to numeric types and strings. == and != apply
// to everything except arrays and structs, and there
// are some restrictions on when it applies to slices.
if !same() || (!integers() && !floats() && !strings()) {
a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = BoolType;
case token.EQL, token.NEQ:
// XXX(Spec) The rules for type checking comparison
// operators are spread across three places that all
// partially overlap with each other: the Comparison
// Compatibility section, the Operators section, and
// the Comparison Operators section. The Operators
// section should just say that operators require
// identical types (as it does currently) except that
// there a few special cases for comparison, which are
// described in section X. Currently it includes just
// one of the four special cases. The Comparison
// Compatibility section and the Comparison Operators
// section should either be merged, or at least the
// Comparison Compatibility section should be
// exclusively about type checking and the Comparison
// Operators section should be exclusively about
// semantics.
// XXX(Spec) Comparison operators: "All comparison
// operators apply to basic types except bools." This
// is very difficult to parse. It's explained much
// better in the Comparison Compatibility section.
// XXX(Spec) Comparison compatibility: "Values of any
// type may be compared to other values of compatible
// static type." Should be *identical* static type.
// XXX(Spec) Comparison compatibility: "Function
// values are equal if they refer to the same
// function." is rather vague. It should probably be
// similar to the way the rule for map values is
// written: Function values are equal if they were
// created by the same execution of a function literal
// or refer to the same function declaration. This is
// *almost* but not quite waht 6g implements. If a
// function literals does not capture any variables,
// then multiple executions of it will result in the
// same closure. Russ says he'll change that.
// TODO(austin) Deal with remaining special cases
if !same() {
a.diagOpTypes(op, origlt, origrt);
return;
}
// Arrays and structs may not be compared to anything.
if _, ok := l.t.(*ArrayType); ok {
a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = BoolType;
default:
log.Crashf("unknown binary operator %v", op);
}
var ok bool;
a.desc, ok = binOpDescs[op];
if !ok {
a.desc = op.String() + " expression";
binOpDescs[op] = a.desc;
}
// Compile
switch op {
case token.ADD:
a.genBinOpAdd(l, r);
case token.SUB:
a.genBinOpSub(l, r);
case token.MUL:
a.genBinOpMul(l, r);
case token.QUO:
// TODO(austin) What if divisor is zero?
// TODO(austin) Clear higher bits that may have
// accumulated in our temporary.
a.genBinOpQuo(l, r);
case token.REM:
// TODO(austin) What if divisor is zero?
// TODO(austin) Clear higher bits that may have
// accumulated in our temporary.
a.genBinOpRem(l, r);
case token.AND:
a.genBinOpAnd(l, r);
case token.OR:
a.genBinOpOr(l, r);
case token.XOR:
a.genBinOpXor(l, r);
case token.AND_NOT:
a.genBinOpAndNot(l, r);
case token.SHL:
if l.t.isIdeal() {
lv := l.asIdealInt()();
rv := r.asIdealInt()();
const maxShift = 99999;
if rv.Cmp(bignum.Int(maxShift)) > 0 {
a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift);
a.t = nil;
return;
}
val := lv.Shl(uint(rv.Value()));
a.evalIdealInt = func() *bignum.Integer { return val };
} else {
a.genBinOpShl(l, r);
}
case token.SHR:
if l.t.isIdeal() {
lv := l.asIdealInt()();
rv := r.asIdealInt()();
val := lv.Shr(uint(rv.Value()));
a.evalIdealInt = func() *bignum.Integer { return val };
} else {
a.genBinOpShr(l, r);
}
case token.LSS:
a.genBinOpLss(l, r);
case token.GTR:
a.genBinOpGtr(l, r);
case token.LEQ:
a.genBinOpLeq(l, r);
case token.GEQ:
a.genBinOpGeq(l, r);
case token.EQL:
a.genBinOpEql(l, r);
case token.NEQ:
a.genBinOpNeq(l, r);
default:
log.Crashf("Compilation of binary op %v not implemented", op);
}
}
func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) {
l, r := a.copyVisit(x.X), a.copyVisit(x.Y);
if l.t == nil || r.t == nil {
return;
}
a.doBinaryExpr(x.Op, l, r);
}
func (a *exprCompiler) DoKeyValueExpr(x *ast.KeyValueExpr) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoEllipsis(x *ast.Ellipsis) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoArrayType(x *ast.ArrayType) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoStructType(x *ast.StructType) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoFuncType(x *ast.FuncType) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoInterfaceType(x *ast.InterfaceType) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoMapType(x *ast.MapType) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoChanType(x *ast.ChanType) {
log.Crash("Not implemented");
}
// TODO(austin) This is a hack to eliminate a circular dependency
// between type.go and expr.go
func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) {
lenExpr := a.compileExpr(b, expr, true);
if lenExpr == nil {
return 0, false;
}
if !lenExpr.t.isInteger() {
a.diagAt(expr, "array size must be an integer");
return 0, false;
}
if lenExpr.t.isIdeal() {
lenExpr = lenExpr.convertTo(IntType);
if lenExpr == nil {
return 0, false;
}
}
switch _ := lenExpr.t.rep().(type) {
case *intType:
return lenExpr.evalInt(nil), true;
case *uintType:
return int64(lenExpr.evalUint(nil)), true;
}
log.Crashf("unexpected integer type %T", lenExpr.t);
return 0, false;
}
func (a *compiler) compileExpr(b *block, expr ast.Expr, constant bool) *exprCompiler {
ec := newExprCompiler(&exprContext{a, b, constant}, expr.Pos());
expr.Visit(ec);
if ec.t == nil {
return nil;
}
return ec;
}
// extractEffect separates out any effects that the expression may
// have, returning a function that will perform those effects and a
// new exprCompiler that is guaranteed to be side-effect free. These
// are the moral equivalents of "temp := &expr" and "*temp".
//
// Implementation limit: The expression must be addressable.
func (a *exprCompiler) extractEffect() (func(f *Frame), *exprCompiler) {
if a.evalAddr == nil {
// This is a much easier case, but the code is
// completely different.
log.Crash("extractEffect only implemented for addressable expressions");
}
// Create temporary
tempBlock := a.block;
tempType := NewPtrType(a.t);
// TODO(austin) These temporaries accumulate in the scope. We
// could enter a temporary block, but the caller has to exit it.
temp := tempBlock.DefineTemp(tempType);
tempIdx := temp.Index;
// Generate "temp := &e"
addr := a.copy();
addr.t = tempType;
addr.genUnaryAddrOf(a);
assign := a.compileAssign(a.pos, tempType, []*exprCompiler{addr}, "", "");
if assign == nil {
log.Crashf("compileAssign type check failed");
}
effect := func(f *Frame) {
tempVal := tempType.Zero();
f.Vars[tempIdx] = tempVal;
assign(tempVal, f);
};
// Generate "*temp"
getTemp := a.copy();
getTemp.t = tempType;
getTemp.genIdentOp(0, tempIdx);
deref := a.copy();
deref.t = a.t;
deref.genStarOp(getTemp);
return effect, deref;
}
/*
* Testing interface
*/
type Expr struct {
t Type;
f func(f *Frame, out Value);
}
func (expr *Expr) Eval(f *Frame) Value {
v := expr.t.Zero();
expr.f(f, v);
return v;
}
func CompileExpr(scope *Scope, expr ast.Expr) (*Expr, os.Error) {
errors := scanner.NewErrorVector();
cc := &compiler{errors};
ec := cc.compileExpr(scope.block, expr, false);
if ec == nil {
return nil, errors.GetError(scanner.Sorted);
}
switch t := ec.t.rep().(type) {
case *boolType:
return &Expr{t, func(f *Frame, out Value) { out.(BoolValue).Set(ec.evalBool(f)) }}, nil;
case *uintType:
return &Expr{t, func(f *Frame, out Value) { out.(UintValue).Set(ec.evalUint(f)) }}, nil;
case *intType:
return &Expr{t, func(f *Frame, out Value) { out.(IntValue).Set(ec.evalInt(f)) }}, nil;
case *idealIntType:
return &Expr{t, func(f *Frame, out Value) { out.(*idealIntV).V = ec.evalIdealInt() }}, nil;
case *floatType:
return &Expr{t, func(f *Frame, out Value) { out.(FloatValue).Set(ec.evalFloat(f)) }}, nil;
case *idealFloatType:
return &Expr{t, func(f *Frame, out Value) { out.(*idealFloatV).V = ec.evalIdealFloat() }}, nil;
case *stringType:
return &Expr{t, func(f *Frame, out Value) { out.(StringValue).Set(ec.evalString(f)) }}, nil;
case *PtrType:
return &Expr{t, func(f *Frame, out Value) { out.(PtrValue).Set(ec.evalPtr(f)) }}, nil;
case *FuncType:
return &Expr{t, func(f *Frame, out Value) { out.(FuncValue).Set(ec.evalFunc(f)) }}, nil;
}
log.Crashf("unexpected type %v", ec.t);
panic();
}
/*
* Operator generators
* Everything below here is MACHINE GENERATED by gen.py genOps
*/
func (a *exprCompiler) genConstant(v Value) {
switch _ := a.t.rep().(type) {
case *boolType:
val := v.(BoolValue).Get();
a.evalBool = func(f *Frame) bool { return val };
case *uintType:
val := v.(UintValue).Get();
a.evalUint = func(f *Frame) uint64 { return val };
case *intType:
val := v.(IntValue).Get();
a.evalInt = func(f *Frame) int64 { return val };
case *idealIntType:
val := v.(IdealIntValue).Get();
a.evalIdealInt = func() *bignum.Integer { return val };
case *floatType:
val := v.(FloatValue).Get();
a.evalFloat = func(f *Frame) float64 { return val };
case *idealFloatType:
val := v.(IdealFloatValue).Get();
a.evalIdealFloat = func() *bignum.Rational { return val };
case *stringType:
val := v.(StringValue).Get();
a.evalString = func(f *Frame) string { return val };
case *ArrayType:
val := v.(ArrayValue).Get();
a.evalArray = func(f *Frame) ArrayValue { return val };
case *PtrType:
val := v.(PtrValue).Get();
a.evalPtr = func(f *Frame) Value { return val };
case *FuncType:
val := v.(FuncValue).Get();
a.evalFunc = func(f *Frame) Func { return val };
default:
log.Crashf("unexpected constant type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genIdentOp(level int, index int) {
a.evalAddr = func(f *Frame) Value { return f.Get(level, index) };
switch _ := a.t.rep().(type) {
case *boolType:
a.evalBool = func(f *Frame) bool { return f.Get(level, index).(BoolValue).Get() };
case *uintType:
a.evalUint = func(f *Frame) uint64 { return f.Get(level, index).(UintValue).Get() };
case *intType:
a.evalInt = func(f *Frame) int64 { return f.Get(level, index).(IntValue).Get() };
case *floatType:
a.evalFloat = func(f *Frame) float64 { return f.Get(level, index).(FloatValue).Get() };
case *stringType:
a.evalString = func(f *Frame) string { return f.Get(level, index).(StringValue).Get() };
case *ArrayType:
a.evalArray = func(f *Frame) ArrayValue { return f.Get(level, index).(ArrayValue).Get() };
case *PtrType:
a.evalPtr = func(f *Frame) Value { return f.Get(level, index).(PtrValue).Get() };
case *FuncType:
a.evalFunc = func(f *Frame) Func { return f.Get(level, index).(FuncValue).Get() };
default:
log.Crashf("unexpected identifier type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genIndexArray(l *exprCompiler, r *exprCompiler) {
lf := l.asArray();
rf := r.asInt();
switch _ := a.t.rep().(type) {
case *boolType:
a.evalBool = func(f *Frame) bool { return lf(f).Elem(rf(f)).(BoolValue).Get() };
case *uintType:
a.evalUint = func(f *Frame) uint64 { return lf(f).Elem(rf(f)).(UintValue).Get() };
case *intType:
a.evalInt = func(f *Frame) int64 { return lf(f).Elem(rf(f)).(IntValue).Get() };
case *floatType:
a.evalFloat = func(f *Frame) float64 { return lf(f).Elem(rf(f)).(FloatValue).Get() };
case *stringType:
a.evalString = func(f *Frame) string { return lf(f).Elem(rf(f)).(StringValue).Get() };
case *ArrayType:
a.evalArray = func(f *Frame) ArrayValue { return lf(f).Elem(rf(f)).(ArrayValue).Get() };
case *PtrType:
a.evalPtr = func(f *Frame) Value { return lf(f).Elem(rf(f)).(PtrValue).Get() };
case *FuncType:
a.evalFunc = func(f *Frame) Func { return lf(f).Elem(rf(f)).(FuncValue).Get() };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genFuncCall(call func(f *Frame) []Value) {
a.exec = func(f *Frame) { call(f) };
switch _ := a.t.rep().(type) {
case *boolType:
a.evalBool = func(f *Frame) bool { return call(f)[0].(BoolValue).Get() };
case *uintType:
a.evalUint = func(f *Frame) uint64 { return call(f)[0].(UintValue).Get() };
case *intType:
a.evalInt = func(f *Frame) int64 { return call(f)[0].(IntValue).Get() };
case *floatType:
a.evalFloat = func(f *Frame) float64 { return call(f)[0].(FloatValue).Get() };
case *stringType:
a.evalString = func(f *Frame) string { return call(f)[0].(StringValue).Get() };
case *ArrayType:
a.evalArray = func(f *Frame) ArrayValue { return call(f)[0].(ArrayValue).Get() };
case *PtrType:
a.evalPtr = func(f *Frame) Value { return call(f)[0].(PtrValue).Get() };
case *FuncType:
a.evalFunc = func(f *Frame) Func { return call(f)[0].(FuncValue).Get() };
case *MultiType:
a.evalMulti = func(f *Frame) []Value { return call(f) };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genStarOp(v *exprCompiler) {
vf := v.asPtr();
a.evalAddr = func(f *Frame) Value { return vf(f) };
switch _ := a.t.rep().(type) {
case *boolType:
a.evalBool = func(f *Frame) bool { return vf(f).(BoolValue).Get() };
case *uintType:
a.evalUint = func(f *Frame) uint64 { return vf(f).(UintValue).Get() };
case *intType:
a.evalInt = func(f *Frame) int64 { return vf(f).(IntValue).Get() };
case *floatType:
a.evalFloat = func(f *Frame) float64 { return vf(f).(FloatValue).Get() };
case *stringType:
a.evalString = func(f *Frame) string { return vf(f).(StringValue).Get() };
case *ArrayType:
a.evalArray = func(f *Frame) ArrayValue { return vf(f).(ArrayValue).Get() };
case *PtrType:
a.evalPtr = func(f *Frame) Value { return vf(f).(PtrValue).Get() };
case *FuncType:
a.evalFunc = func(f *Frame) Func { return vf(f).(FuncValue).Get() };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genUnaryOpNeg(v *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
vf := v.asUint();
a.evalUint = func(f *Frame) uint64 { return -vf(f) };
case *intType:
vf := v.asInt();
a.evalInt = func(f *Frame) int64 { return -vf(f) };
case *idealIntType:
vf := v.asIdealInt();
val := vf().Neg();
a.evalIdealInt = func() *bignum.Integer { return val };
case *floatType:
vf := v.asFloat();
a.evalFloat = func(f *Frame) float64 { return -vf(f) };
case *idealFloatType:
vf := v.asIdealFloat();
val := vf().Neg();
a.evalIdealFloat = func() *bignum.Rational { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genUnaryOpNot(v *exprCompiler) {
switch _ := a.t.rep().(type) {
case *boolType:
vf := v.asBool();
a.evalBool = func(f *Frame) bool { return !vf(f) };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genUnaryOpXor(v *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
vf := v.asUint();
a.evalUint = func(f *Frame) uint64 { return ^vf(f) };
case *intType:
vf := v.asInt();
a.evalInt = func(f *Frame) int64 { return ^vf(f) };
case *idealIntType:
vf := v.asIdealInt();
val := vf().Neg().Sub(bignum.Int(1));
a.evalIdealInt = func() *bignum.Integer { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpAdd(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) + rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) + rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Add(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalFloat = func(f *Frame) float64 { return lf(f) + rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Add(rf());
a.evalIdealFloat = func() *bignum.Rational { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalString = func(f *Frame) string { return lf(f) + rf(f) };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpSub(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) - rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) - rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Sub(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalFloat = func(f *Frame) float64 { return lf(f) - rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Sub(rf());
a.evalIdealFloat = func() *bignum.Rational { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpMul(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) * rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) * rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Mul(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalFloat = func(f *Frame) float64 { return lf(f) * rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Mul(rf());
a.evalIdealFloat = func() *bignum.Rational { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpQuo(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) / rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) / rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Quo(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalFloat = func(f *Frame) float64 { return lf(f) / rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Quo(rf());
a.evalIdealFloat = func() *bignum.Rational { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpRem(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) % rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) % rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Rem(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpAnd(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) & rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) & rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().And(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpOr(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) | rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) | rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Or(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpXor(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) ^ rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) ^ rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Xor(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpAndNot(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) &^ rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalInt = func(f *Frame) int64 { return lf(f) &^ rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().AndNot(rf());
a.evalIdealInt = func() *bignum.Integer { return val };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpShl(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) << rf(f) };
case *intType:
lf := l.asInt();
rf := r.asUint();
a.evalInt = func(f *Frame) int64 { return lf(f) << rf(f) };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpShr(l *exprCompiler, r *exprCompiler) {
switch _ := a.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalUint = func(f *Frame) uint64 { return lf(f) >> rf(f) };
case *intType:
lf := l.asInt();
rf := r.asUint();
a.evalInt = func(f *Frame) int64 { return lf(f) >> rf(f) };
default:
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
}
}
func (a *exprCompiler) genBinOpLss(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalBool = func(f *Frame) bool { return lf(f) < rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalBool = func(f *Frame) bool { return lf(f) < rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Cmp(rf()) < 0;
a.evalBool = func(f *Frame) bool { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalBool = func(f *Frame) bool { return lf(f) < rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Cmp(rf()) < 0;
a.evalBool = func(f *Frame) bool { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalBool = func(f *Frame) bool { return lf(f) < rf(f) };
default:
log.Crashf("unexpected left operand type %v at %v", l.t, a.pos);
}
}
func (a *exprCompiler) genBinOpGtr(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalBool = func(f *Frame) bool { return lf(f) > rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalBool = func(f *Frame) bool { return lf(f) > rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Cmp(rf()) > 0;
a.evalBool = func(f *Frame) bool { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalBool = func(f *Frame) bool { return lf(f) > rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Cmp(rf()) > 0;
a.evalBool = func(f *Frame) bool { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalBool = func(f *Frame) bool { return lf(f) > rf(f) };
default:
log.Crashf("unexpected left operand type %v at %v", l.t, a.pos);
}
}
func (a *exprCompiler) genBinOpLeq(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalBool = func(f *Frame) bool { return lf(f) <= rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalBool = func(f *Frame) bool { return lf(f) <= rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Cmp(rf()) <= 0;
a.evalBool = func(f *Frame) bool { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalBool = func(f *Frame) bool { return lf(f) <= rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Cmp(rf()) <= 0;
a.evalBool = func(f *Frame) bool { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalBool = func(f *Frame) bool { return lf(f) <= rf(f) };
default:
log.Crashf("unexpected left operand type %v at %v", l.t, a.pos);
}
}
func (a *exprCompiler) genBinOpGeq(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.rep().(type) {
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalBool = func(f *Frame) bool { return lf(f) >= rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalBool = func(f *Frame) bool { return lf(f) >= rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Cmp(rf()) >= 0;
a.evalBool = func(f *Frame) bool { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalBool = func(f *Frame) bool { return lf(f) >= rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Cmp(rf()) >= 0;
a.evalBool = func(f *Frame) bool { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalBool = func(f *Frame) bool { return lf(f) >= rf(f) };
default:
log.Crashf("unexpected left operand type %v at %v", l.t, a.pos);
}
}
func (a *exprCompiler) genBinOpEql(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.rep().(type) {
case *boolType:
lf := l.asBool();
rf := r.asBool();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Cmp(rf()) == 0;
a.evalBool = func(f *Frame) bool { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Cmp(rf()) == 0;
a.evalBool = func(f *Frame) bool { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
case *PtrType:
lf := l.asPtr();
rf := r.asPtr();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
case *FuncType:
lf := l.asFunc();
rf := r.asFunc();
a.evalBool = func(f *Frame) bool { return lf(f) == rf(f) };
default:
log.Crashf("unexpected left operand type %v at %v", l.t, a.pos);
}
}
func (a *exprCompiler) genBinOpNeq(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.rep().(type) {
case *boolType:
lf := l.asBool();
rf := r.asBool();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
case *uintType:
lf := l.asUint();
rf := r.asUint();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
case *intType:
lf := l.asInt();
rf := r.asInt();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
case *idealIntType:
lf := l.asIdealInt();
rf := r.asIdealInt();
val := lf().Cmp(rf()) != 0;
a.evalBool = func(f *Frame) bool { return val };
case *floatType:
lf := l.asFloat();
rf := r.asFloat();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
case *idealFloatType:
lf := l.asIdealFloat();
rf := r.asIdealFloat();
val := lf().Cmp(rf()) != 0;
a.evalBool = func(f *Frame) bool { return val };
case *stringType:
lf := l.asString();
rf := r.asString();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
case *PtrType:
lf := l.asPtr();
rf := r.asPtr();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
case *FuncType:
lf := l.asFunc();
rf := r.asFunc();
a.evalBool = func(f *Frame) bool { return lf(f) != rf(f) };
default:
log.Crashf("unexpected left operand type %v at %v", l.t, a.pos);
}
}
func genAssign(lt Type, r *exprCompiler) (func(lv Value, f *Frame)) {
switch _ := lt.rep().(type) {
case *boolType:
rf := r.asBool();
return func(lv Value, f *Frame) { lv.(BoolValue).Set(rf(f)) };
case *uintType:
rf := r.asUint();
return func(lv Value, f *Frame) { lv.(UintValue).Set(rf(f)) };
case *intType:
rf := r.asInt();
return func(lv Value, f *Frame) { lv.(IntValue).Set(rf(f)) };
case *floatType:
rf := r.asFloat();
return func(lv Value, f *Frame) { lv.(FloatValue).Set(rf(f)) };
case *stringType:
rf := r.asString();
return func(lv Value, f *Frame) { lv.(StringValue).Set(rf(f)) };
case *ArrayType:
rf := r.asArray();
return func(lv Value, f *Frame) { lv.Assign(rf(f)) };
case *PtrType:
rf := r.asPtr();
return func(lv Value, f *Frame) { lv.(PtrValue).Set(rf(f)) };
case *FuncType:
rf := r.asFunc();
return func(lv Value, f *Frame) { lv.(FuncValue).Set(rf(f)) };
default:
log.Crashf("unexpected left operand type %v at %v", lt, r.pos);
}
panic();
}