Commit 27b0c351 authored by Austin Clements's avatar Austin Clements

Implement comparison operators.

R=rsc
APPROVED=rsc
DELTA=304  (281 added, 0 deleted, 23 changed)
OCL=32361
CL=32382
parent 2210a643
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment