1
0
mirror of https://github.com/golang/go synced 2024-10-04 22:21:22 -06:00
go/usr/austin/eval/expr.go
Austin Clements 816e3da26d 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
2009-07-15 17:56:17 -07:00

845 lines
23 KiB
Go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package eval
import (
"bignum";
"eval";
"go/ast";
"go/token";
"log";
"strconv";
"strings";
)
// An exprContext stores information used throughout the compilation
// of an entire expression.
type exprContext struct {
scope *Scope;
constant bool;
// TODO(austin) Error list
}
// 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;
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
// messages. Only necessary if t != nil.
desc string;
}
func newExprCompiler(c *exprContext, pos token.Position) *exprCompiler {
return &exprCompiler{
exprContext: c,
pos: pos,
desc: "<missing description>"
};
}
func (a *exprCompiler) fork(x ast.Expr) *exprCompiler {
ec := newExprCompiler(a.exprContext, x.Pos());
x.Visit(ec);
return ec;
}
func (a *exprCompiler) diag(format string, args ...) {
diag(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);
}
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) {
// 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) {
def, dscope := a.scope.Lookup(x.Value);
if def == nil {
a.diag("%s: undefined", x.Value);
return;
}
switch def := def.(type) {
case *Constant:
a.t = def.Type;
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";
case *Variable:
if a.constant {
a.diag("expression must be a constant");
return;
}
a.t = def.Type;
defidx := def.Index;
a.genIdent(def.Type, dscope, defidx);
a.evalAddr = func (f *Frame) Value {
return f.Get(dscope, 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] != '\'' {
// Shouldn't get past the parser
log.Crashf("unexpected character literal %s at %v", x.Value, x.Pos());
}
v, mb, tail, err := strconv.UnquoteChar(string(x.Value[1:len(x.Value)]), '\'');
if err != nil {
a.diag("illegal character literal, %v", err);
return;
}
if tail != "'" {
a.diag("character literal must contain only one character");
return;
}
a.doIdealInt(bignum.Int(int64(v)));
a.desc = "character literal";
}
func (a *exprCompiler) DoFloatLit(x *ast.FloatLit) {
a.t = IdealFloatType;
f, _, _2 := bignum.RatFromString(string(x.Value), 0);
a.evalIdealFloat = func () *bignum.Rational { return f };
a.desc = "float literal";
}
func (a *exprCompiler) doString(s string) {
a.t = StringType;
a.evalString = func (*Frame) string { return s };
}
func (a *exprCompiler) DoStringLit(x *ast.StringLit) {
s, err := strconv.Unquote(string(x.Value));
if err != nil {
a.diag("illegal string literal, %v", err);
return;
}
a.doString(s);
a.desc = "string literal";
}
func (a *exprCompiler) DoStringList(x *ast.StringList) {
ss := make([]string, len(x.Strings));
for i := 0; i < len(x.Strings); i++ {
s, err := strconv.Unquote(string(x.Strings[i].Value));
if err != nil {
a.diag("illegal string literal, %v", err);
return;
}
ss[i] = s;
}
a.doString(strings.Join(ss, ""));
a.desc = "string literal";
}
func (a *exprCompiler) DoFuncLit(x *ast.FuncLit) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoCompositeLit(x *ast.CompositeLit) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoParenExpr(x *ast.ParenExpr) {
x.X.Visit(a);
}
func (a *exprCompiler) DoSelectorExpr(x *ast.SelectorExpr) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoIndexExpr(x *ast.IndexExpr) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoTypeAssertExpr(x *ast.TypeAssertExpr) {
log.Crash("Not implemented");
}
func (a *exprCompiler) DoCallExpr(x *ast.CallExpr) {
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) {
v := a.fork(x.X);
if v.t == nil {
return;
}
switch vt := v.t.(type) {
case *PtrType:
a.t = vt.Elem();
a.genStarOp(v);
vf := v.asPtr();
a.evalAddr = func (f *Frame) Value { return vf(f) };
a.desc = "* expression";
default:
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) {
switch x.Op {
case token.SUB:
// Negation
v := a.fork(x.X);
if v.t == nil {
return;
}
if !v.t.isInteger() && !v.t.isFloat() {
a.diagOpType(x.Op, v.t);
return;
}
a.t = v.t;
a.genUnaryOpNeg(v);
a.desc = "- expression";
case token.AND:
// Address-of
v := a.fork(x.X);
if v.t == nil {
return;
}
// 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.
at := NewPtrType(v.t);
a.t = at;
vf := v.evalAddr;
a.evalPtr = func (f *Frame) Value { return vf(f) };
a.desc = "& expression";
default:
log.Crashf("Unary op %v not implemented", x.Op);
}
}
// 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.
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;
// It is erroneous 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.(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 := newExprCompiler(a.exprContext, a.pos);
res.t = t;
res.desc = a.desc;
switch t := t.(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) 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)
func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) {
l, r := a.fork(x.X), a.fork(x.Y);
if l.t == nil || r.t == nil {
return;
}
// 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.
// 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 x.Op != token.SHL && x.Op != token.SHR {
if l.t.isInteger() && !l.t.isIdeal() && r.t.isIdeal() {
r = r.convertTo(l.t);
} else if r.t.isInteger() && !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
compat := func() bool {
return l.t.compatible(r.t);
};
integers := func() bool {
return l.t.isInteger() && r.t.isInteger();
};
floats := func() bool {
return l.t.isFloat() && r.t.isFloat();
};
strings := func() bool {
// TODO(austin) Deal with named types
return l.t == StringType && r.t == StringType;
};
booleans := func() bool {
// TODO(austin) Deal with named types
return l.t == BoolType && r.t == BoolType;
};
// Type check
switch x.Op {
case token.ADD:
if !compat() || (!integers() && !floats() && !strings()) {
a.diagOpTypes(x.Op, origlt, origrt);
return;
}
a.t = l.t;
case token.SUB, token.MUL, token.QUO:
if !compat() || (!integers() && !floats()) {
a.diagOpTypes(x.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(x.Op, origlt, origrt);
return;
}
a.t = l.t;
case token.SHL, token.SHR:
// 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() {
r = r.convertTo(UintType);
if r == nil {
return;
}
}
if !integers() {
a.diagOpTypes(x.Op, origlt, origrt);
return;
}
if _, ok := r.t.literal().(*uintType); !ok {
a.diag("right operand of shift must be unsigned");
return;
}
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.
// TODO(austin) Named bool 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:
// ... booleans may be compared only for equality or
// inequality.
if l.t.literal() == BoolType || r.t.literal() == BoolType {
a.diagOpTypes(x.Op, origlt, origrt);
return;
}
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) 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", x.Op);
// TODO(austin) Unnamed bool? Named bool?
a.t = BoolType;
default:
log.Crashf("unknown binary operator %v", x.Op);
}
var ok bool;
a.desc, ok = opDescs[x.Op];
if !ok {
a.desc = x.Op.String() + " expression";
opDescs[x.Op] = a.desc;
}
// Compile
switch x.Op {
case token.ADD:
a.genBinOpAdd(l, r);
case token.SUB:
a.genBinOpSub(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);
default:
log.Crashf("Compilation of binary op %v not implemented", x.Op);
}
}
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");
}
func compileExpr(expr ast.Expr, scope *Scope) *exprCompiler {
ec := newExprCompiler(&exprContext{scope, false}, expr.Pos());
expr.Visit(ec);
if ec.t == nil {
return nil;
}
return ec;
}
/*
* Public interface
*/
type Expr struct {
f func (f *Frame) Value;
}
func (expr *Expr) Eval(f *Frame) Value {
return expr.f(f);
}
func CompileExpr(expr ast.Expr, scope *Scope) *Expr {
ec := compileExpr(expr, scope);
if ec == nil {
return nil;
}
// 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;
}