]> Cypherpunks repositories - gostls13.git/commitdiff
Implement comparison operators.
authorAustin Clements <aclements@csail.mit.edu>
Tue, 28 Jul 2009 23:40:23 +0000 (16:40 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Tue, 28 Jul 2009 23:40:23 +0000 (16:40 -0700)
R=rsc
APPROVED=rsc
DELTA=304  (281 added, 0 deleted, 23 changed)
OCL=32361
CL=32382

usr/austin/eval/expr.go

index 309caa0abf4c0ba6d50a0851d3dc10f630159a35..7aebedb48ef3f31a029bc42486354f427246ce0c 100644 (file)
@@ -79,6 +79,12 @@ 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 {
@@ -1065,35 +1071,70 @@ func (a *exprCompiler) doBinaryExpr(op token.Token, l, r *exprCompiler) {
                a.t = BoolType;
 
        case token.LSS, token.GTR, token.LEQ, token.GEQ:
-               // ... booleans may be compared only for equality or
-               // inequality.
+               // 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 l.t.isBoolean() || r.t.isBoolean() {
+               if !same() || (!integers() && !floats() && !strings()) {
                        a.diagOpTypes(op, origlt, origrt);
                        return;
                }
+               a.t = BoolType;
 
-               fallthrough;
        case token.EQL, token.NEQ:
-               // When comparing two operands of channel type, the
-               // channel value types must be compatible but the
-               // channel direction is ignored.
-
-               // XXX(Spec) Operators: "When comparing two operands
-               // of channel type, the channel value types must be
-               // compatible but the channel direction is ignored."
-               // By "compatible" this really means "comparison
-               // compatible".  Really, the rules for type checking
-               // comparison operators are entirely different from
-               // other binary operators, but this just barely hints
-               // at that.
+               // 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."
-               // "except bools" is really weird here, since this is
-               // actually explained in the Comparison compatibility
-               // section.
-               log.Crashf("Binary op %v not implemented", op);
+               // 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:
@@ -1168,6 +1209,24 @@ func (a *exprCompiler) doBinaryExpr(op token.Token, l, r *exprCompiler) {
                        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);
        }
@@ -1789,6 +1848,228 @@ func (a *exprCompiler) genBinOpShr(l *exprCompiler, r *exprCompiler) {
        }
 }
 
+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: