// 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"; "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; evalStruct func(f *Frame) StructValue; evalPtr func(f *Frame) Value; evalFunc func(f *Frame) Func; evalSlice func(f *Frame) Slice; evalMap func(f *Frame) Map; evalMulti func(f *Frame) []Value; // Map index expressions permit special forms of assignment, // for which we need to know the Map and key. evalMapValue func(f *Frame) (Map, interface{}); // 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: "" }; } 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) asStruct() (func(f *Frame) StructValue) { if a.evalStruct == nil { log.Crashf("tried to get %v node as StructType", a.t); } return a.evalStruct; } 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) asSlice() (func(f *Frame) Slice) { if a.evalSlice == nil { log.Crashf("tried to get %v node as SliceType", a.t); } return a.evalSlice; } func (a *exprCompiler) asMap() (func(f *Frame) Map) { if a.evalMap == nil { log.Crashf("tried to get %v node as MapType", a.t); } return a.evalMap; } 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; } func (a *exprCompiler) asInterface() (func(f *Frame) interface {}) { switch _ := a.t.lit().(type) { case *boolType: sf := a.asBool(); return func(f *Frame) interface {} { return sf(f) }; case *uintType: sf := a.asUint(); return func(f *Frame) interface {} { return sf(f) }; case *intType: sf := a.asInt(); return func(f *Frame) interface {} { return sf(f) }; case *floatType: sf := a.asFloat(); return func(f *Frame) interface {} { return sf(f) }; case *stringType: sf := a.asString(); return func(f *Frame) interface {} { return sf(f) }; case *PtrType: sf := a.asPtr(); return func(f *Frame) interface {} { return sf(f) }; case *FuncType: sf := a.asFunc(); return func(f *Frame) interface {} { return sf(f) }; case *MapType: sf := a.asMap(); return func(f *Frame) interface {} { return sf(f) }; default: log.Crashf("unexpected expression node type %v at %v", a.t, a.pos); } panic(); } /* * 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.lit().(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.lit().(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; } func (a *exprCompiler) genStarOp(v *exprCompiler) { a.genValue(v.asPtr()); } /* * 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; // Whether map special assignment forms are allowed. allowMap bool; // Whether this is a "r, ok = a[x]" assignment. isMapUnpack 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; } func (a *assignCompiler) allowMapForms(nls int) { a.allowMap = true; // Update unpacking info if this is r, ok = a[x] if nls == 2 && len(a.rs) == 1 && a.rs[0].evalMapValue != nil { a.isUnpack = true; a.rmt = NewMultiType([]Type {a.rs[0].t, BoolType}); a.isMapUnpack = true; } } // 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; // If this is an unpack, create a temporary to store the // multi-value and replace the RHS with expressions to pull // out values from the temporary. Technically, this is only // necessary when we need to perform assignment conversions. var effect func(f *Frame); if isUnpack { // TODO(austin) Is it safe to exit the block? What if // there are multiple unpacks in one statement, such // as for function calls? //bc := a.rs[0].block.enterChild(); //defer bc.exit(); // This leaks a slot, but is definitely safe. bc := a.rs[0].block; temp := bc.DefineSlot(a.rmt); tempIdx := temp.Index; if a.isMapUnpack { rf := a.rs[0].evalMapValue; vt := a.rmt.Elems[0]; effect = func(f *Frame) { m, k := rf(f); v := m.Elem(k); found := boolV(true); if v == nil { found = boolV(false); v = vt.Zero(); } f.Vars[tempIdx] = multiV([]Value {v, &found}); }; } else { rf := a.rs[0].asMulti(); effect = func(f *Frame) { f.Vars[tempIdx] = multiV(rf(f)); }; } orig := a.rs[0]; a.rs = make([]*exprCompiler, len(a.rmt.Elems)); for i, t := range a.rmt.Elems { if t.isIdeal() { log.Crashf("Right side of unpack contains ideal: %s", rmt); } a.rs[i] = orig.copy(); a.rs[i].t = t; index := i; a.rs[i].genValue(func(f *Frame) Value { return f.Vars[tempIdx].(multiV)[index] }); } } // Now len(a.rs) == len(a.rmt) and we've reduced any unpacking // to multi-assignment. // TODO(austin) Deal with assignment special cases. // Values of any type may always be assigned to variables of // compatible static type. for i, lt := range lmt.Elems { 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() { a.rs[i] = a.rs[i].convertTo(lmt.Elems[i]); if a.rs[i] == nil { bad = true; continue; } rt = a.rs[i].t; } // A pointer p to an array can be assigned to a slice // variable v with compatible element type if the type // of p or v is unnamed. if rpt, ok := rt.lit().(*PtrType); ok { if at, ok := rpt.Elem.lit().(*ArrayType); ok { if lst, ok := lt.lit().(*SliceType); ok { if lst.Elem.compat(at.Elem, false) && (rt.lit() == Type(rt) || lt.lit() == Type(lt)) { rf := a.rs[i].asPtr(); a.rs[i] = a.rs[i].copy(); a.rs[i].t = lt; len := at.Len; a.rs[i].evalSlice = func(f *Frame) Slice { return Slice{rf(f).(ArrayValue), len, len}; }; rt = a.rs[i].t; } } } } if !lt.compat(rt, false) { 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 if !isMT { // Case 1 return genAssign(lt, a.rs[0]); } // Case 2 or 3 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) { if effect != nil { effect(f); } lmv := lv.(multiV); for i, a := range as { a(lmv[i], f); } }; } // 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) { // Ideal strings don't have a named type but they are // compatible with type string. // TODO(austin) Use unnamed string type. 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) { v := a.copyVisit(x.X); if v.t == nil { return; } // mark marks a field that matches the selector name. It // tracks the best depth found so far and whether more than // one field has been found at that depth. bestDepth := -1; ambig := false; amberr := ""; mark := func(depth int, pathName string) { switch { case bestDepth == -1 || depth < bestDepth: bestDepth = depth; ambig = false; amberr = ""; case depth == bestDepth: ambig = true; default: log.Crashf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth); } amberr += "\n\t" + pathName[1:len(pathName)]; }; name := x.Sel.Value; visited := make(map[Type] bool); // find recursively searches for the named field, starting at // type t. If it finds the named field, it returns a function // which takes an exprCompiler that retrieves a value of type // 't' and fills 'a' to retrieve the named field. We delay // exprCompiler construction to avoid filling in anything // until we're sure we have the right field, and to avoid // producing lots of garbage exprCompilers as we search. var find func(Type, int, string) (func (*exprCompiler)); find = func(t Type, depth int, pathName string) (func (*exprCompiler)) { // Don't bother looking if we've found something shallower if bestDepth != -1 && bestDepth < depth { return nil; } // Don't check the same type twice and avoid loops if _, ok := visited[t]; ok { return nil; } visited[t] = true; // Implicit dereference deref := false; if ti, ok := t.(*PtrType); ok { deref = true; t = ti.Elem; } // If it's a named type, look for methods if ti, ok := t.(*NamedType); ok { method, ok := ti.methods[name]; if ok { mark(depth, pathName + "." + name); log.Crash("Methods not implemented"); } t = ti.def; } // If it's a struct type, check fields and embedded types var builder func(*exprCompiler); if t, ok := t.(*StructType); ok { for i, f := range t.Elems { var this *exprCompiler; var sub func(*exprCompiler); switch { case f.Name == name: mark(depth, pathName + "." + name); this = a; sub = func(*exprCompiler) {}; case f.Anonymous: sub = find(f.Type, depth+1, pathName + "." + f.Name); if sub == nil { continue; } this = a.copy(); default: continue; } // We found something. Create a // builder for accessing this field. ft := f.Type; index := i; builder = func(parent *exprCompiler) { this.t = ft; var evalAddr func(f *Frame) Value; if deref { pf := parent.asPtr(); evalAddr = func(f *Frame) Value { return pf(f).(StructValue).Field(index); }; } else { pf := parent.asStruct(); evalAddr = func(f *Frame) Value { return pf(f).Field(index); }; } this.genValue(evalAddr); sub(this); }; } } return builder; }; builder := find(v.t, 0, ""); if builder == nil { a.diag("type %v has no field or method %s", v.t, name); return; } if ambig { a.diag("field %s is ambiguous in type %v%s", name, v.t, amberr); return; } a.desc = "selector expression"; builder(v); } 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.lit().(*PtrType); ok { if et, ok := lt.Elem.lit().(*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.lit().(type) { case *ArrayType: at = lt.Elem; intIndex = true; maxIndex = lt.Len; case *SliceType: at = lt.Elem; intIndex = true; case *stringType: at = Uint8Type; intIndex = true; case *MapType: at = lt.Elem; if r.t.isIdeal() { r = r.convertTo(lt.Key); if r == nil { return; } } if !lt.Key.compat(r.t, false) { a.diag("cannot use %s as index into %s", r.t, lt); return; } 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.lit().(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; a.desc = "index expression"; // Compile switch lt := l.t.lit().(type) { case *ArrayType: // 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 *SliceType: // TODO(austin) Bounds check // TODO(austin) Can this be done with genValue? a.genIndexSlice(l, r); lf := l.asSlice(); rf := r.asInt(); a.evalAddr = func(f *Frame) Value { return lf(f).Base.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)]); } case *MapType: // TODO(austin) Bounds check lf := l.asMap(); rf := r.asInterface(); a.genValue(func(f *Frame) Value { m := lf(f); k := rf(f); e := m.Elem(k); if e == nil { // TODO(austin) Use an exception panic("key ", k, " not found in map"); } return e; }); // genValue makes things addressable, but map values // aren't addressable. a.evalAddr = nil; a.evalMapValue = func(f *Frame) (Map, interface{}) { return lf(f), rf(f); }; default: log.Crashf("unexpected left operand type %T", l.t.lit()); } } 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.lit().(*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.lit().(type) { case *PtrType: a.t = vt.Elem; // TODO(austin) Deal with nil pointers 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; } } } // Useful type predicates // TODO(austin) CL 33668 mandates identical types except for comparisons. compat := func() bool { return l.t.compat(r.t, false); }; 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 !compat() || (!integers() && !floats() && !strings()) { a.diagOpTypes(op, origlt, origrt); return; } a.t = l.t; case token.SUB, token.MUL, token.QUO: if !compat() || (!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 !compat() || !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.lit().(*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 !compat() || (!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: "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 !compat() { a.diagOpTypes(op, origlt, origrt); return; } // Arrays and structs may not be compared to anything. // TODO(austin) Use a multi-type switch if _, ok := l.t.(*ArrayType); ok { a.diagOpTypes(op, origlt, origrt); return; } if _, ok := l.t.(*StructType); 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; } // XXX(Spec) Are ideal floats with no fractional part okay? if lenExpr.t.isIdeal() { lenExpr = lenExpr.convertTo(IntType); if lenExpr == nil { return 0, false; } } if !lenExpr.t.isInteger() { a.diagAt(expr, "array size must be an integer"); return 0, false; } switch _ := lenExpr.t.lit().(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" (or "temp := // &expr" and "*temp" for addressable exprs). Because this creates a // temporary variable, the caller should create a temporary block for // the compilation of this expression and the evaluation of the // results. func (a *exprCompiler) extractEffect(errOp string) (func(f *Frame), *exprCompiler) { // Create "&a" if a is addressable rhs := a; if a.evalAddr != nil { rhs = a.copy(); rhs.t = NewPtrType(a.t); rhs.genUnaryAddrOf(a); } // Create temp tempBlock := a.block; ac, ok := a.checkAssign(a.pos, []*exprCompiler{rhs}, errOp, ""); if !ok { return nil, nil; } if len(ac.rmt.Elems) != 1 { a.diag("multi-valued expression not allowed in %s", errOp); return nil, nil; } tempType := ac.rmt.Elems[0]; if tempType.isIdeal() { // It's too bad we have to duplicate this rule. switch { case tempType.isInteger(): tempType = IntType; case tempType.isFloat(): tempType = FloatType; default: log.Crashf("unexpected ideal type %v", tempType); } } temp := tempBlock.DefineSlot(tempType); tempIdx := temp.Index; // Create "temp := rhs" assign := ac.compile(tempType); 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" or "*temp" getTemp := a.copy(); getTemp.t = tempType; getTemp.genIdentOp(0, tempIdx); if a.evalAddr == nil { return effect, getTemp; } 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.lit().(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 *ArrayType: return &Expr{t, func(f *Frame, out Value) { out.(ArrayValue).Assign(ec.evalArray(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; case *SliceType: return &Expr{t, func(f *Frame, out Value) { out.(SliceValue).Set(ec.evalSlice(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.lit().(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 *StructType: val := v.(StructValue).Get(); a.evalStruct = func(f *Frame) StructValue { 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 }; case *SliceType: val := v.(SliceValue).Get(); a.evalSlice = func(f *Frame) Slice { return val }; case *MapType: val := v.(MapValue).Get(); a.evalMap = func(f *Frame) Map { 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.lit().(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 *StructType: a.evalStruct = func(f *Frame) StructValue { return f.Get(level, index).(StructValue).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() }; case *SliceType: a.evalSlice = func(f *Frame) Slice { return f.Get(level, index).(SliceValue).Get() }; case *MapType: a.evalMap = func(f *Frame) Map { return f.Get(level, index).(MapValue).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.lit().(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 *StructType: a.evalStruct = func(f *Frame) StructValue { return lf(f).Elem(rf(f)).(StructValue).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() }; case *SliceType: a.evalSlice = func(f *Frame) Slice { return lf(f).Elem(rf(f)).(SliceValue).Get() }; case *MapType: a.evalMap = func(f *Frame) Map { return lf(f).Elem(rf(f)).(MapValue).Get() }; default: log.Crashf("unexpected result type %v at %v", a.t, a.pos); } } func (a *exprCompiler) genIndexSlice(l *exprCompiler, r *exprCompiler) { lf := l.asSlice(); rf := r.asInt(); switch _ := a.t.lit().(type) { case *boolType: a.evalBool = func(f *Frame) bool { return lf(f).Base.Elem(rf(f)).(BoolValue).Get() }; case *uintType: a.evalUint = func(f *Frame) uint64 { return lf(f).Base.Elem(rf(f)).(UintValue).Get() }; case *intType: a.evalInt = func(f *Frame) int64 { return lf(f).Base.Elem(rf(f)).(IntValue).Get() }; case *floatType: a.evalFloat = func(f *Frame) float64 { return lf(f).Base.Elem(rf(f)).(FloatValue).Get() }; case *stringType: a.evalString = func(f *Frame) string { return lf(f).Base.Elem(rf(f)).(StringValue).Get() }; case *ArrayType: a.evalArray = func(f *Frame) ArrayValue { return lf(f).Base.Elem(rf(f)).(ArrayValue).Get() }; case *StructType: a.evalStruct = func(f *Frame) StructValue { return lf(f).Base.Elem(rf(f)).(StructValue).Get() }; case *PtrType: a.evalPtr = func(f *Frame) Value { return lf(f).Base.Elem(rf(f)).(PtrValue).Get() }; case *FuncType: a.evalFunc = func(f *Frame) Func { return lf(f).Base.Elem(rf(f)).(FuncValue).Get() }; case *SliceType: a.evalSlice = func(f *Frame) Slice { return lf(f).Base.Elem(rf(f)).(SliceValue).Get() }; case *MapType: a.evalMap = func(f *Frame) Map { return lf(f).Base.Elem(rf(f)).(MapValue).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.lit().(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 *StructType: a.evalStruct = func(f *Frame) StructValue { return call(f)[0].(StructValue).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 *SliceType: a.evalSlice = func(f *Frame) Slice { return call(f)[0].(SliceValue).Get() }; case *MapType: a.evalMap = func(f *Frame) Map { return call(f)[0].(MapValue).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) genValue(vf func(*Frame) Value) { a.evalAddr = vf; switch _ := a.t.lit().(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 *StructType: a.evalStruct = func(f *Frame) StructValue { return vf(f).(StructValue).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() }; case *SliceType: a.evalSlice = func(f *Frame) Slice { return vf(f).(SliceValue).Get() }; case *MapType: a.evalMap = func(f *Frame) Map { return vf(f).(MapValue).Get() }; default: log.Crashf("unexpected result type %v at %v", a.t, a.pos); } } func (a *exprCompiler) genUnaryOpNeg(v *exprCompiler) { switch _ := a.t.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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.lit().(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) }; case *MapType: lf := l.asMap(); rf := r.asMap(); 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.lit().(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) }; case *MapType: lf := l.asMap(); rf := r.asMap(); 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.lit().(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 *StructType: rf := r.asStruct(); 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)) }; case *SliceType: rf := r.asSlice(); return func(lv Value, f *Frame) { lv.(SliceValue).Set(rf(f)) }; case *MapType: rf := r.asMap(); return func(lv Value, f *Frame) { lv.(MapValue).Set(rf(f)) }; default: log.Crashf("unexpected left operand type %v at %v", lt, r.pos); } panic(); }