Commit 816e3da2 authored by Austin Clements's avatar Austin Clements

Make Value always represent an l-value and never a generic

container for values.

Instead of having one evaluator function that returns a
generic Value, there is now an evaluator function for each
generalized type that simply returns a native type.

The compiler is more type-safe now because there are almost no
type conversions at evaluation time and it's impossible to
invoke a nil evaluator function during evaluation.  This also
makes ideals and pointers really clean.

As an added bonus, expression evaluation should be faster
because it doesn't require heap allocation for every
intermediate value, type switches, or lots of conversions to
and from Value.  It also involves fewer function calls.

R=rsc
APPROVED=rsc
DELTA=431  (280 added, 115 deleted, 36 changed)
OCL=31705
CL=31709
parent 3fc7cfd5
...@@ -136,3 +136,5 @@ type Frame struct { ...@@ -136,3 +136,5 @@ type Frame struct {
Scope *Scope; Scope *Scope;
Vars []Value; Vars []Value;
} }
func (f *Frame) Get(s *Scope, index int) Value
...@@ -30,20 +30,30 @@ type exprCompiler struct { ...@@ -30,20 +30,30 @@ type exprCompiler struct {
*exprContext; *exprContext;
pos token.Position; pos token.Position;
t Type; t Type;
// TODO(austin) Should there be separate f's for each specific // Evaluate this node as the given type.
// Value interface? We spend a lot of time calling f's and evalBool func (f *Frame) bool;
// just blindly casting the result since we already know its type. evalUint func (f *Frame) uint64;
f func (f *Frame) Value; evalInt func (f *Frame) int64;
evalIdealInt func () *bignum.Integer;
evalFloat func (f *Frame) float64;
evalIdealFloat func () *bignum.Rational;
evalString func (f *Frame) string;
evalPtr func (f *Frame) Value;
// Evaluate to the "address of" this value; that is, the
// settable Value object. nil for expressions whose address
// cannot be taken.
evalAddr func (f *Frame) Value;
// A short string describing this expression for error // A short string describing this expression for error
// messages. Only necessary if t != nil. // messages. Only necessary if t != nil.
desc string; desc string;
// True if the address-of operator can be applied to this
// result.
addressable bool;
} }
func newExprCompiler(c *exprContext, pos token.Position) *exprCompiler { func newExprCompiler(c *exprContext, pos token.Position) *exprCompiler {
return &exprCompiler{c, pos, nil, nil, "<missing description>", false}; return &exprCompiler{
exprContext: c,
pos: pos,
desc: "<missing description>"
};
} }
func (a *exprCompiler) fork(x ast.Expr) *exprCompiler { func (a *exprCompiler) fork(x ast.Expr) *exprCompiler {
...@@ -64,10 +74,85 @@ func (a *exprCompiler) diagOpTypes(op token.Token, lt Type, rt Type) { ...@@ -64,10 +74,85 @@ 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); a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt);
} }
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) 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) DoBadExpr(x *ast.BadExpr) { func (a *exprCompiler) DoBadExpr(x *ast.BadExpr) {
// Do nothing. Already reported by parser. // Do nothing. Already reported by parser.
} }
func (a *exprCompiler) genIdent(t Type, s *Scope, index int) {
switch _ := t.literal().(type) {
case *boolType:
a.evalBool = func (f *Frame) bool { return f.Get(s, index).(BoolValue).Get() };
case *uintType:
a.evalUint = func (f *Frame) uint64 { return f.Get(s, index).(UintValue).Get() };
case *intType:
a.evalInt = func (f *Frame) int64 { return f.Get(s, index).(IntValue).Get() };
case *floatType:
a.evalFloat = func (f *Frame) float64 { return f.Get(s, index).(FloatValue).Get() };
case *stringType:
a.evalString = func (f *Frame) string { return f.Get(s, index).(StringValue).Get() };
case *PtrType:
a.evalPtr = func (f *Frame) Value { return f.Get(s, index).(PtrValue).Get() };
default:
log.Crashf("unexpected variable type %v at %v", t.literal(), a.pos);
}
}
func (a *exprCompiler) DoIdent(x *ast.Ident) { func (a *exprCompiler) DoIdent(x *ast.Ident) {
def, dscope := a.scope.Lookup(x.Value); def, dscope := a.scope.Lookup(x.Value);
if def == nil { if def == nil {
...@@ -77,7 +162,16 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) { ...@@ -77,7 +162,16 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) {
switch def := def.(type) { switch def := def.(type) {
case *Constant: case *Constant:
a.t = def.Type; a.t = def.Type;
a.f = func (*Frame) Value { return def.Value }; switch _ := a.t.literal().(type) {
case *idealIntType:
val := def.Value.(IdealIntValue).Get();
a.evalIdealInt = func () *bignum.Integer { return val; };
case *idealFloatType:
val := def.Value.(IdealFloatValue).Get();
a.evalIdealFloat = func () *bignum.Rational { return val; };
default:
log.Crashf("unexpected constant type: %v", a.t);
}
a.desc = "constant"; a.desc = "constant";
case *Variable: case *Variable:
if a.constant { if a.constant {
...@@ -86,15 +180,11 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) { ...@@ -86,15 +180,11 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) {
} }
a.t = def.Type; a.t = def.Type;
defidx := def.Index; defidx := def.Index;
a.f = func (f *Frame) Value { a.genIdent(def.Type, dscope, defidx);
// TODO(austin) Make Frame do this? a.evalAddr = func (f *Frame) Value {
for f.Scope != dscope { return f.Get(dscope, defidx);
f = f.Outer;
}
return f.Vars[defidx];
}; };
a.desc = "variable"; a.desc = "variable";
a.addressable = true;
case Type: case Type:
a.diag("type %v used as expression", x.Value); a.diag("type %v used as expression", x.Value);
default: default:
...@@ -104,8 +194,7 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) { ...@@ -104,8 +194,7 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) {
func (a *exprCompiler) doIdealInt(i *bignum.Integer) { func (a *exprCompiler) doIdealInt(i *bignum.Integer) {
a.t = IdealIntType; a.t = IdealIntType;
val := &idealIntV{i}; a.evalIdealInt = func () *bignum.Integer { return i };
a.f = func (*Frame) Value { return val };
} }
func (a *exprCompiler) DoIntLit(x *ast.IntLit) { func (a *exprCompiler) DoIntLit(x *ast.IntLit) {
...@@ -134,16 +223,14 @@ func (a *exprCompiler) DoCharLit(x *ast.CharLit) { ...@@ -134,16 +223,14 @@ func (a *exprCompiler) DoCharLit(x *ast.CharLit) {
func (a *exprCompiler) DoFloatLit(x *ast.FloatLit) { func (a *exprCompiler) DoFloatLit(x *ast.FloatLit) {
a.t = IdealFloatType; a.t = IdealFloatType;
i, _, _2 := bignum.RatFromString(string(x.Value), 0); f, _, _2 := bignum.RatFromString(string(x.Value), 0);
val := &idealFloatV{i}; a.evalIdealFloat = func () *bignum.Rational { return f };
a.f = func (*Frame) Value { return val };
a.desc = "float literal"; a.desc = "float literal";
} }
func (a *exprCompiler) doString(s string) { func (a *exprCompiler) doString(s string) {
a.t = StringType; a.t = StringType;
val := stringV(s); a.evalString = func (*Frame) string { return s };
a.f = func (*Frame) Value { return &val };
} }
func (a *exprCompiler) DoStringLit(x *ast.StringLit) { func (a *exprCompiler) DoStringLit(x *ast.StringLit) {
...@@ -198,6 +285,26 @@ func (a *exprCompiler) DoCallExpr(x *ast.CallExpr) { ...@@ -198,6 +285,26 @@ func (a *exprCompiler) DoCallExpr(x *ast.CallExpr) {
log.Crash("Not implemented"); log.Crash("Not implemented");
} }
func (a *exprCompiler) genStarOp(v *exprCompiler) {
vf := v.asPtr();
switch _ := v.t.literal().(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 *PtrType:
a.evalPtr = func (f *Frame) Value { return vf(f).(PtrValue).Get() };
default:
log.Crashf("unexpected operand type %v at %v", v.t.literal(), a.pos);
}
}
func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) { func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) {
v := a.fork(x.X); v := a.fork(x.X);
if v.t == nil { if v.t == nil {
...@@ -207,16 +314,40 @@ func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) { ...@@ -207,16 +314,40 @@ func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) {
switch vt := v.t.(type) { switch vt := v.t.(type) {
case *PtrType: case *PtrType:
a.t = vt.Elem(); a.t = vt.Elem();
vf := v.f; a.genStarOp(v);
a.f = func (f *Frame) Value { return vf(f).(PtrValue).Get() }; vf := v.asPtr();
a.evalAddr = func (f *Frame) Value { return vf(f) };
a.desc = "* expression"; a.desc = "* expression";
a.addressable = true;
default: default:
a.diagOpType(token.MUL, v.t); a.diagOpType(token.MUL, v.t);
} }
} }
func (a *exprCompiler) genUnaryOpNeg(v *exprCompiler) {
switch _ := v.t.literal().(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 operand type %v at %v", v.t.literal(), a.pos);
}
}
func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) { func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) {
switch x.Op { switch x.Op {
case token.SUB: case token.SUB:
...@@ -226,33 +357,15 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) { ...@@ -226,33 +357,15 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) {
return; return;
} }
a.t = v.t; if !v.t.isInteger() && !v.t.isFloat() {
vf := v.f;
switch vt := v.t.literal().(type) {
case *uintType:
a.f = func (f *Frame) Value {
return vt.value(-vf(f).(UintValue).Get());
};
case *intType:
a.f = func (f *Frame) Value {
return vt.value(-vf(f).(IntValue).Get());
};
case *idealIntType:
val := vt.value(vf(nil).(IdealIntValue).Get().Neg());
a.f = func (f *Frame) Value { return val };
case *floatType:
a.f = func (f *Frame) Value {
return vt.value(-vf(f).(FloatValue).Get());
};
case *idealFloatType:
val := vt.value(vf(nil).(IdealFloatValue).Get().Neg());
a.f = func (f *Frame) Value { return val };
default:
a.t = nil;
a.diagOpType(x.Op, v.t); a.diagOpType(x.Op, v.t);
return; return;
} }
a.t = v.t;
a.genUnaryOpNeg(v);
a.desc = "- expression";
case token.AND: case token.AND:
// Address-of // Address-of
v := a.fork(x.X); v := a.fork(x.X);
...@@ -264,7 +377,7 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) { ...@@ -264,7 +377,7 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) {
// the address of its operand, which must be a // the address of its operand, which must be a
// variable, pointer indirection, field selector, or // variable, pointer indirection, field selector, or
// array or slice indexing operation. // array or slice indexing operation.
if !v.addressable { if v.evalAddr == nil {
a.diag("cannot take the address of %s", v.desc); a.diag("cannot take the address of %s", v.desc);
return; return;
} }
...@@ -276,8 +389,8 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) { ...@@ -276,8 +389,8 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) {
at := NewPtrType(v.t); at := NewPtrType(v.t);
a.t = at; a.t = at;
vf := v.f; vf := v.evalAddr;
a.f = func (f *Frame) Value { return at.value(vf(f)) }; a.evalPtr = func (f *Frame) Value { return vf(f) };
a.desc = "& expression"; a.desc = "& expression";
default: default:
...@@ -293,7 +406,6 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler { ...@@ -293,7 +406,6 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler {
log.Crashf("attempted to convert from %v, expected ideal", a.t); log.Crashf("attempted to convert from %v, expected ideal", a.t);
} }
val := a.f(nil);
var rat *bignum.Rational; var rat *bignum.Rational;
// It is erroneous to assign a value with a non-zero // It is erroneous to assign a value with a non-zero
...@@ -302,13 +414,14 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler { ...@@ -302,13 +414,14 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler {
// represented by the type of the variable. // represented by the type of the variable.
switch a.t { switch a.t {
case IdealFloatType: case IdealFloatType:
rat = val.(IdealFloatValue).Get(); rat = a.asIdealFloat()();
if t.isInteger() && !rat.IsInt() { if t.isInteger() && !rat.IsInt() {
a.diag("constant %v truncated to integer", ratToString(rat)); a.diag("constant %v truncated to integer", ratToString(rat));
return nil; return nil;
} }
case IdealIntType: case IdealIntType:
rat = bignum.MakeRat(val.(IdealIntValue).Get(), bignum.Nat(1)); i := a.asIdealInt()();
rat = bignum.MakeRat(i, bignum.Nat(1));
default: default:
log.Crashf("unexpected ideal type %v", a.t); log.Crashf("unexpected ideal type %v", a.t);
} }
...@@ -326,38 +439,128 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler { ...@@ -326,38 +439,128 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler {
} }
// Convert rat to type t. // Convert rat to type t.
res := newExprCompiler(a.exprContext, a.pos);
res.t = t;
res.desc = a.desc;
switch t := t.(type) { switch t := t.(type) {
case *uintType: case *uintType:
n, d := rat.Value(); n, d := rat.Value();
f := n.Quo(bignum.MakeInt(false, d)); f := n.Quo(bignum.MakeInt(false, d));
v := f.Abs().Value(); v := f.Abs().Value();
val = t.value(v); res.evalUint = func (*Frame) uint64 { return v };
case *intType: case *intType:
n, d := rat.Value(); n, d := rat.Value();
f := n.Quo(bignum.MakeInt(false, d)); f := n.Quo(bignum.MakeInt(false, d));
v := f.Value(); v := f.Value();
val = t.value(v); res.evalInt = func (*Frame) int64 { return v };
case *idealIntType: case *idealIntType:
n, d := rat.Value(); n, d := rat.Value();
f := n.Quo(bignum.MakeInt(false, d)); f := n.Quo(bignum.MakeInt(false, d));
val = t.value(f); res.evalIdealInt = func () *bignum.Integer { return f };
case *floatType: case *floatType:
n, d := rat.Value(); n, d := rat.Value();
v := float64(n.Value())/float64(d.Value()); v := float64(n.Value())/float64(d.Value());
val = t.value(v); res.evalFloat = func (*Frame) float64 { return v };
case *idealFloatType: case *idealFloatType:
val = t.value(rat); res.evalIdealFloat = func () *bignum.Rational { return rat };
default: default:
log.Crashf("cannot convert to type %T", t); log.Crashf("cannot convert to type %T", t);
} }
res := newExprCompiler(a.exprContext, a.pos);
res.t = t;
res.f = func (*Frame) Value { return val };
res.desc = a.desc;
return res; return res;
} }
func (a *exprCompiler) genBinOpAdd(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.literal().(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 left operand type %v at %v", l.t.literal(), a.pos);
}
}
func (a *exprCompiler) genBinOpSub(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.literal().(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 left operand type %v at %v", l.t.literal(), a.pos);
}
}
func (a *exprCompiler) genBinOpQuo(l *exprCompiler, r *exprCompiler) {
switch _ := l.t.literal().(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 left operand type %v at %v", l.t.literal(), a.pos);
}
}
var opDescs = make(map[token.Token] string) var opDescs = make(map[token.Token] string)
func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) {
...@@ -539,94 +742,18 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { ...@@ -539,94 +742,18 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) {
} }
// Compile // Compile
// TODO(austin) There has got to be a better way to do this.
lf := l.f;
rf := r.f;
switch x.Op { switch x.Op {
case token.ADD: case token.ADD:
switch lt := l.t.literal().(type) { a.genBinOpAdd(l, r);
case *uintType:
// TODO(austin) lt.value allocates. It would
// be awesome if we could avoid that for
// intermediate values. That might be
// possible if we pass the closure a place to
// store its result.
a.f = func (f *Frame) Value {
return lt.value(lf(f).(UintValue).Get() + rf(f).(UintValue).Get());
};
case *intType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(IntValue).Get() + rf(f).(IntValue).Get());
};
case *idealIntType:
val := lt.value(lf(nil).(IdealIntValue).Get().Add(rf(nil).(IdealIntValue).Get()));
a.f = func (f *Frame) Value { return val };
case *floatType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(FloatValue).Get() + rf(f).(FloatValue).Get());
};
case *idealFloatType:
val := lt.value(lf(nil).(IdealFloatValue).Get().Add(rf(nil).(IdealFloatValue).Get()));
a.f = func (f *Frame) Value { return val };
case *stringType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(StringValue).Get() + rf(f).(StringValue).Get());
};
default:
// Shouldn't have passed type checking
log.Crashf("unexpected left operand type %v at %v", l.t.literal(), x.Pos());
}
case token.SUB: case token.SUB:
switch lt := l.t.literal().(type) { a.genBinOpSub(l, r);
case *uintType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(UintValue).Get() - rf(f).(UintValue).Get());
};
case *intType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(IntValue).Get() - rf(f).(IntValue).Get());
};
case *idealIntType:
val := lt.value(lf(nil).(IdealIntValue).Get().Sub(rf(nil).(IdealIntValue).Get()));
a.f = func (f *Frame) Value { return val };
case *floatType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(FloatValue).Get() - rf(f).(FloatValue).Get());
};
case *idealFloatType:
val := lt.value(lf(nil).(IdealFloatValue).Get().Sub(rf(nil).(IdealFloatValue).Get()));
a.f = func (f *Frame) Value { return val };
default:
// Shouldn't have passed type checking
log.Crashf("unexpected left operand type %v at %v", l.t.literal(), x.Pos());
}
case token.QUO: case token.QUO:
// TODO(austin) What if divisor is zero? // TODO(austin) What if divisor is zero?
switch lt := l.t.literal().(type) { // TODO(austin) Clear higher bits that may have
case *uintType: // accumulated in our temporary.
a.f = func (f *Frame) Value { a.genBinOpQuo(l, r);
return lt.value(lf(f).(UintValue).Get() / rf(f).(UintValue).Get());
};
case *intType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(IntValue).Get() / rf(f).(IntValue).Get());
};
case *idealIntType:
val := lt.value(lf(nil).(IdealIntValue).Get().Quo(rf(nil).(IdealIntValue).Get()));
a.f = func (f *Frame) Value { return val };
case *floatType:
a.f = func (f *Frame) Value {
return lt.value(lf(f).(FloatValue).Get() / rf(f).(FloatValue).Get());
};
case *idealFloatType:
val := lt.value(lf(nil).(IdealFloatValue).Get().Quo(rf(nil).(IdealFloatValue).Get()));
a.f = func (f *Frame) Value { return val };
default:
// Shouldn't have passed type checking
log.Crashf("unexpected left operand type %v at %v", l.t.literal(), x.Pos());
}
default: default:
log.Crashf("Compilation of binary op %v not implemented", x.Op); log.Crashf("Compilation of binary op %v not implemented", x.Op);
...@@ -691,5 +818,27 @@ func CompileExpr(expr ast.Expr, scope *Scope) *Expr { ...@@ -691,5 +818,27 @@ func CompileExpr(expr ast.Expr, scope *Scope) *Expr {
if ec == nil { if ec == nil {
return nil; return nil;
} }
return &Expr{ec.f}; // TODO(austin) This still uses Value as a generic container
// and is the only user of the 'value' methods on each type.
// Need to figure out a better way to do this.
switch t := ec.t.(type) {
case *boolType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalBool(f)) }};
case *uintType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalUint(f)) }};
case *intType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalInt(f)) }};
case *idealIntType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalIdealInt()) }};
case *floatType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalFloat(f)) }};
case *idealFloatType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalIdealFloat()) }};
case *stringType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalString(f)) }};
case *PtrType:
return &Expr{func (f *Frame) Value { return t.value(ec.evalPtr(f)) }};
}
log.Crashf("unexpected type %v", ec.t);
return nil;
} }
...@@ -52,3 +52,10 @@ func (s *Scope) Lookup(name string) (Def, *Scope) { ...@@ -52,3 +52,10 @@ func (s *Scope) Lookup(name string) (Def, *Scope) {
} }
return nil, nil; return nil, nil;
} }
func (f *Frame) Get(s *Scope, index int) Value {
for f.Scope != s {
f = f.Outer;
}
return f.Vars[index];
}
...@@ -59,6 +59,8 @@ func (boolType) String() string { ...@@ -59,6 +59,8 @@ func (boolType) String() string {
return "bool"; return "bool";
} }
func (t *boolType) value(v bool) BoolValue
type uintType struct { type uintType struct {
commonType; commonType;
......
...@@ -32,6 +32,11 @@ func (v *boolV) Set(x bool) { ...@@ -32,6 +32,11 @@ func (v *boolV) Set(x bool) {
*v = boolV(x); *v = boolV(x);
} }
func (t *boolType) value(v bool) BoolValue {
res := boolV(v);
return &res;
}
/* /*
* Uint * Uint
*/ */
...@@ -145,8 +150,8 @@ func (v *uintptrV) Set(x uint64) { ...@@ -145,8 +150,8 @@ func (v *uintptrV) Set(x uint64) {
} }
func (t *uintType) value(v uint64) UintValue { func (t *uintType) value(v uint64) UintValue {
// TODO(austin) This executes are run-time, even though // TODO(austin) The 'value' methods are only used for
// virtually all of the logic can be done at type-check time. // testing right now. Get rid of them.
// TODO(austin) Deal with named types // TODO(austin) Deal with named types
switch Type(t) { switch Type(t) {
case Uint8Type: case Uint8Type:
......
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