1
0
mirror of https://github.com/golang/go synced 2024-11-12 10:00:25 -07:00

Implement cap, len, and make, as well as the general framework

for built-in functions and type conversions.  Extract out
common operations on expression nodes for converting them to
ints and implicitly dereferencing arrays.

R=rsc
APPROVED=rsc
DELTA=442  (365 added, 50 deleted, 27 changed)
OCL=34064
CL=34064
This commit is contained in:
Austin Clements 2009-08-28 18:03:03 -07:00
parent e667e8a4f7
commit c90bc34d75
4 changed files with 392 additions and 77 deletions

View File

@ -67,5 +67,21 @@ type KeyNotFound struct {
}
func (e KeyNotFound) String() string {
return fmt.Sprintf("key %s not found in map", e.Key);
return fmt.Sprintf("key '%v' not found in map", e.Key);
}
type NegativeLength struct {
Len int64;
}
func (e NegativeLength) String() string {
return fmt.Sprintf("negative length: %d", e.Len);
}
type NegativeCapacity struct {
Len int64;
}
func (e NegativeCapacity) String() string {
return fmt.Sprintf("negative capacity: %d", e.Len);
}

View File

@ -47,6 +47,10 @@ type expr struct {
// Execute this expression as a statement. Only expressions
// that are valid expression statements should set this.
exec func(f *Frame);
// If this expression is a type, this is its compiled type.
// This is only permitted in the function position of a call
// expression. In this case, t should be nil.
valType Type;
// A short string describing this expression for error
// messages.
desc string;
@ -289,6 +293,59 @@ func (a *expr) convertTo(t Type) *expr {
return res;
}
// convertToInt converts this expression to an integer, if possible,
// or produces an error if not. This accepts ideal ints, uints, and
// ints. If max is not -1, produces an error if possible if the value
// exceeds max. If negErr is not "", produces an error if possible if
// the value is negative.
func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr {
switch _ := a.t.lit().(type) {
case *idealIntType:
val := a.asIdealInt()();
if negErr != "" && val.IsNeg() {
a.diag("negative %s: %s", negErr, val);
return nil;
}
if max != -1 && val.Cmp(bignum.Int(max)) >= 0 {
a.diag("index %s exceeds length %d", val, max);
return nil;
}
return a.convertTo(IntType);
case *uintType:
// Convert to int
na := a.newExpr(IntType, a.desc);
af := a.asUint();
na.evalInt = func(f *Frame) int64 {
return int64(af(f));
};
return na;
case *intType:
// Good as is
return a;
}
a.diag("illegal operand type for %s\n\t%v", errOp, a.t);
return nil;
}
// derefArray returns an expression of array type if the given
// expression is a *array type. Otherwise, returns the given
// expression.
func (a *expr) derefArray() *expr {
if pt, ok := a.t.lit().(*PtrType); ok {
if at, ok := pt.Elem.lit().(*ArrayType); ok {
deref := a.compileStarExpr(a);
if deref == nil {
log.Crashf("failed to dereference *array");
}
return deref;
}
}
return a;
}
/*
* Assignments
*/
@ -556,7 +613,11 @@ type exprCompiler struct {
constant bool;
}
func (a *exprCompiler) compile(x ast.Expr) *expr {
// compile compiles an expression AST. callCtx should be true if this
// AST is in the function position of a function call node; it allows
// the returned expression to be a type or a built-in function (which
// otherwise result in errors).
func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr {
ei := &exprInfo{a.compiler, x.Pos()};
switch x := x.(type) {
@ -595,22 +656,23 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
// Types
case *ast.ArrayType:
goto notimpl;
// TODO(austin) Use a multi-type case
goto typeexpr;
case *ast.ChanType:
goto notimpl;
goto typeexpr;
case *ast.Ellipsis:
goto notimpl;
goto typeexpr;
case *ast.FuncType:
goto notimpl;
goto typeexpr;
case *ast.InterfaceType:
goto notimpl;
goto typeexpr;
case *ast.MapType:
goto notimpl;
goto typeexpr;
// Remaining expressions
case *ast.BadExpr:
@ -619,18 +681,23 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
return nil;
case *ast.BinaryExpr:
l, r := a.compile(x.X), a.compile(x.Y);
l, r := a.compile(x.X, false), a.compile(x.Y, false);
if l == nil || r == nil {
return nil;
}
return ei.compileBinaryExpr(x.Op, l, r);
case *ast.CallExpr:
l := a.compile(x.Fun);
l := a.compile(x.Fun, true);
args := make([]*expr, len(x.Args));
bad := false;
for i, arg := range x.Args {
args[i] = a.compile(arg);
if i == 0 && l.t == Type(makeType) {
argei := &exprInfo{a.compiler, arg.Pos()};
args[i] = argei.exprFromType(a.compileType(a.block, arg));
} else {
args[i] = a.compile(arg, false);
}
if args[i] == nil {
bad = true;
}
@ -642,17 +709,25 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
a.diagAt(x, "function call in constant context");
return nil;
}
return ei.compileCallExpr(a.block, l, args);
if l.valType != nil {
a.diagAt(x, "type conversions not implemented");
return nil;
} else if ft, ok := l.t.(*FuncType); ok && ft.builtin != "" {
return ei.compileBuiltinCallExpr(a.block, ft, args);
} else {
return ei.compileCallExpr(a.block, l, args);
}
case *ast.Ident:
return ei.compileIdent(a.block, a.constant, x.Value);
return ei.compileIdent(a.block, a.constant, callCtx, x.Value);
case *ast.IndexExpr:
if x.End != nil {
a.diagAt(x, "slice expression not implemented");
return nil;
}
l, r := a.compile(x.X), a.compile(x.Index);
l, r := a.compile(x.X, false), a.compile(x.Index, false);
if l == nil || r == nil {
return nil;
}
@ -662,27 +737,33 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
goto notimpl;
case *ast.ParenExpr:
return a.compile(x.X);
return a.compile(x.X, callCtx);
case *ast.SelectorExpr:
v := a.compile(x.X);
v := a.compile(x.X, false);
if v == nil {
return nil;
}
return ei.compileSelectorExpr(v, x.Sel.Value);
case *ast.StarExpr:
v := a.compile(x.X);
// We pass down our call context because this could be
// a pointer type (and thus a type conversion)
v := a.compile(x.X, callCtx);
if v == nil {
return nil;
}
if v.valType != nil {
// Turns out this was a pointer type, not a dereference
return ei.exprFromType(NewPtrType(v.valType));
}
return ei.compileStarExpr(v);
case *ast.StringList:
strings := make([]*expr, len(x.Strings));
bad := false;
for i, s := range x.Strings {
strings[i] = a.compile(s);
strings[i] = a.compile(s, false);
if strings[i] == nil {
bad = true;
}
@ -699,7 +780,7 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
goto notimpl;
case *ast.UnaryExpr:
v := a.compile(x.X);
v := a.compile(x.X, false);
if v == nil {
return nil;
}
@ -708,12 +789,28 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
log.Crashf("unexpected ast node type %T", x);
panic();
typeexpr:
if !callCtx {
a.diagAt(x, "type used as expression");
return nil;
}
return ei.exprFromType(a.compileType(a.block, x));
notimpl:
a.diagAt(x, "%T expression node not implemented", x);
return nil;
}
func (a *exprInfo) compileIdent(b *block, constant bool, name string) *expr {
func (a *exprInfo) exprFromType(t Type) *expr {
if t == nil {
return nil;
}
expr := a.newExpr(nil, "type");
expr.valType = t;
return expr;
}
func (a *exprInfo) compileIdent(b *block, constant bool, callCtx bool, name string) *expr {
level, def := b.Lookup(name);
if def == nil {
a.diag("%s: undefined", name);
@ -722,7 +819,18 @@ func (a *exprInfo) compileIdent(b *block, constant bool, name string) *expr {
switch def := def.(type) {
case *Constant:
expr := a.newExpr(def.Type, "constant");
expr.genConstant(def.Value);
if ft, ok := def.Type.(*FuncType); ok && ft.builtin != "" {
// XXX(Spec) I don't think anything says that
// built-in functions can't be used as values.
if !callCtx {
a.diag("built-in function %s cannot be used as a value", ft.builtin);
return nil;
}
// Otherwise, we leave the evaluators empty
// because this is handled specially
} else {
expr.genConstant(def.Value);
}
return expr;
case *Variable:
if constant {
@ -731,6 +839,9 @@ func (a *exprInfo) compileIdent(b *block, constant bool, name string) *expr {
}
return a.compileVariable(level, def);
case Type:
if callCtx {
return a.exprFromType(def);
}
a.diag("type %v used as expression", name);
return nil;
}
@ -936,15 +1047,7 @@ func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr {
func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// Type check object
if lt, ok := l.t.lit().(*PtrType); ok {
if et, ok := lt.Elem.lit().(*ArrayType); ok {
// Automatic dereference
l = a.compileStarExpr(l);
if l == nil {
return nil;
}
}
}
l = l.derefArray();
var at Type;
intIndex := false;
@ -987,36 +1090,8 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// 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() {
a.diag("negative index: %s", val);
return nil;
}
if maxIndex != -1 && val.Cmp(bignum.Int(maxIndex)) >= 0 {
a.diag("index %s exceeds length %d", val, maxIndex);
return nil;
}
r = r.convertTo(IntType);
if r == nil {
return nil;
}
case *uintType:
// Convert to int
nr := a.newExpr(IntType, r.desc);
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);
r = r.convertToInt(maxIndex, "index", "index");
if r == nil {
return nil;
}
}
@ -1070,6 +1145,9 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
expr.genValue(func(f *Frame) Value {
m := lf(f);
k := rf(f);
if m == nil {
Abort(NilPointer{});
}
e := m.Elem(k);
if e == nil {
Abort(KeyNotFound{k});
@ -1080,7 +1158,7 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// aren't addressable.
expr.evalAddr = nil;
expr.evalMapValue = func(f *Frame) (Map, interface{}) {
// TODO(austin) Key check?
// TODO(austin) Key check? nil check?
return lf(f), rf(f);
};
@ -1092,11 +1170,6 @@ func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
}
func (a *exprInfo) compileCallExpr(b *block, l *expr, as []*expr) *expr {
// TODO(austin) Type conversions look like calls, but will
// fail in DoIdent right now.
//
// TODO(austin) Magic built-in functions
//
// TODO(austin) Variadic functions.
// Type check
@ -1162,6 +1235,193 @@ func (a *exprInfo) compileCallExpr(b *block, l *expr, as []*expr) *expr {
return expr;
}
func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *expr {
checkCount := func(min, max int) bool {
if len(as) < min {
a.diag("not enough arguments to %s", ft.builtin);
return false;
} else if len(as) > max {
a.diag("too many arguments to %s", ft.builtin);
return false;
}
return true;
};
switch ft {
case capType:
if !checkCount(1, 1) {
return nil;
}
arg := as[0].derefArray();
expr := a.newExpr(IntType, "function call");
switch t := arg.t.lit().(type) {
case *ArrayType:
// TODO(austin) It would be nice if this could
// be a constant int.
v := t.Len;
expr.evalInt = func(f *Frame) int64 {
return v;
};
case *SliceType:
vf := arg.asSlice();
expr.evalInt = func(f *Frame) int64 {
return vf(f).Cap;
};
//case *ChanType:
default:
a.diag("illegal argument type for cap function\n\t%v", arg.t);
return nil;
}
return expr;
case lenType:
if !checkCount(1, 1) {
return nil;
}
arg := as[0].derefArray();
expr := a.newExpr(IntType, "function call");
switch t := arg.t.lit().(type) {
case *stringType:
vf := arg.asString();
expr.evalInt = func(f *Frame) int64 {
return int64(len(vf(f)));
};
case *ArrayType:
// TODO(austin) It would be nice if this could
// be a constant int.
v := t.Len;
expr.evalInt = func(f *Frame) int64 {
return v;
};
case *SliceType:
vf := arg.asSlice();
expr.evalInt = func(f *Frame) int64 {
return vf(f).Len;
};
case *MapType:
vf := arg.asMap();
expr.evalInt = func(f *Frame) int64 {
// XXX(Spec) What's the len of an
// uninitialized map?
m := vf(f);
if m == nil {
return 0;
}
return m.Len();
};
//case *ChanType:
default:
a.diag("illegal argument type for len function\n\t%v", arg.t);
return nil;
}
return expr;
case makeType:
if !checkCount(1, 3) {
return nil;
}
// XXX(Spec) What are the types of the
// arguments? Do they have to be ints? 6g
// accepts any integral type.
var lenexpr, capexpr *expr;
var lenf, capf func(f *Frame) int64;
if len(as) > 1 {
lenexpr = as[1].convertToInt(-1, "length", "make function");
if lenexpr == nil {
return nil;
}
lenf = lenexpr.asInt();
}
if len(as) > 2 {
capexpr = as[2].convertToInt(-1, "capacity", "make function");
if capexpr == nil {
return nil;
}
capf = capexpr.asInt();
}
switch t := as[0].valType.lit().(type) {
case *SliceType:
// A new, initialized slice value for a given
// element type T is made using the built-in
// function make, which takes a slice type and
// parameters specifying the length and
// optionally the capacity.
if !checkCount(2, 3) {
return nil;
}
et := t.Elem;
expr := a.newExpr(t, "function call");
expr.evalSlice = func(f *Frame) Slice {
l := lenf(f);
// XXX(Spec) What if len or cap is
// negative? The runtime panics.
if l < 0 {
Abort(NegativeLength{l});
}
c := l;
if capf != nil {
c = capf(f);
if c < 0 {
Abort(NegativeCapacity{c});
}
// XXX(Spec) What happens if
// len > cap? The runtime
// sets cap to len.
if l > c {
c = l;
}
}
base := arrayV(make([]Value, c));
for i := int64(0); i < c; i++ {
base[i] = et.Zero();
}
return Slice{&base, l, c};
};
return expr;
case *MapType:
// A new, empty map value is made using the
// built-in function make, which takes the map
// type and an optional capacity hint as
// arguments.
if !checkCount(1, 2) {
return nil;
}
expr := a.newExpr(t, "function call");
expr.evalMap = func(f *Frame) Map {
if lenf == nil {
return make(evalMap);
}
l := lenf(f);
return make(evalMap, l);
};
return expr;
//case *ChanType:
default:
a.diag("illegal argument type for make function\n\t%v", as[0].valType);
return nil;
}
case closeType, closedType, newType, panicType, paniclnType, printType, printlnType:
a.diag("built-in function %s not implemented", ft.builtin);
return nil;
}
log.Crashf("unexpected built-in function '%s'", ft.builtin);
panic();
}
func (a *exprInfo) compileStarExpr(v *expr) *expr {
switch vt := v.t.lit().(type) {
case *PtrType:
@ -1646,7 +1906,7 @@ func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) {
func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr {
ec := &exprCompiler{a, b, constant};
nerr := a.numError();
e := ec.compile(expr);
e := ec.compile(expr, false);
if e == nil && nerr == a.numError() {
log.Crashf("expression compilation failed without reporting errors");
}

View File

@ -175,15 +175,6 @@ var (
UintptrType = universe.DefineType("uintptr", universePos, &uintType{commonType{}, 0, true, "uintptr"});
)
func init() {
// To avoid portability issues all numeric types are distinct
// except byte, which is an alias for uint8.
// Make byte an alias for the named type uint8. Type aliases
// are otherwise impossible in Go, so just hack it here.
universe.defs["byte"] = universe.defs["uint8"];
}
func (t *uintType) compat(o Type, conv bool) bool {
t2, ok := o.lit().(*uintType);
return ok && t == t2;;
@ -730,11 +721,26 @@ type FuncType struct {
In []Type;
Variadic bool;
Out []Type;
builtin string;
}
var funcTypes = newTypeArrayMap()
var variadicFuncTypes = newTypeArrayMap()
// Create singleton function types for magic built-in functions
var (
capType = &FuncType{builtin: "cap"};
closeType = &FuncType{builtin: "close"};
closedType = &FuncType{builtin: "closed"};
lenType = &FuncType{builtin: "len"};
makeType = &FuncType{builtin: "make"};
newType = &FuncType{builtin: "new"};
panicType = &FuncType{builtin: "panic"};
paniclnType = &FuncType{builtin: "panicln"};
printType = &FuncType{builtin: "print"};
printlnType = &FuncType{builtin: "println"};
)
// Two function types are identical if they have the same number of
// parameters and result values and if corresponding parameter and
// result types are identical. All "..." parameters have identical
@ -757,7 +763,7 @@ func NewFuncType(in []Type, variadic bool, out []Type) *FuncType {
return tI.(*FuncType);
}
t := &FuncType{commonType{}, in, variadic, out};
t := &FuncType{commonType{}, in, variadic, out, ""};
outMap.Put(out, t);
return t;
}
@ -807,6 +813,9 @@ func typeListString(ts []Type, ns []*ast.Ident) string {
}
func (t *FuncType) String() string {
if t.builtin != "" {
return "built-in function " + t.builtin;
}
args := typeListString(t.In, nil);
if t.Variadic {
if len(args) > 0 {
@ -894,6 +903,8 @@ func (t *SliceType) String() string {
}
func (t *SliceType) Zero() Value {
// The value of an uninitialized slice is nil. The length and
// capacity of a nil slice are 0.
return &sliceV{Slice{nil, 0, 0}};
}
@ -940,6 +951,7 @@ func (t *MapType) String() string {
}
func (t *MapType) Zero() Value {
// The value of an uninitialized map is nil.
return &mapV{nil};
}
@ -1097,3 +1109,28 @@ func (t *MultiType) Zero() Value {
}
return multiV(res);
}
/*
* Initialize the universe
*/
func init() {
// To avoid portability issues all numeric types are distinct
// except byte, which is an alias for uint8.
// Make byte an alias for the named type uint8. Type aliases
// are otherwise impossible in Go, so just hack it here.
universe.defs["byte"] = universe.defs["uint8"];
// Built-in functions
universe.DefineConst("cap", universePos, capType, nil);
universe.DefineConst("close", universePos, closeType, nil);
universe.DefineConst("closed", universePos, closedType, nil);
universe.DefineConst("len", universePos, lenType, nil);
universe.DefineConst("make", universePos, makeType, nil);
universe.DefineConst("new", universePos, newType, nil);
universe.DefineConst("panic", universePos, panicType, nil);
universe.DefineConst("panicln", universePos, paniclnType, nil);
universe.DefineConst("print", universePos, printType, nil);
universe.DefineConst("println", universePos, printlnType, nil);
}

View File

@ -258,6 +258,8 @@ func (a *typeCompiler) compileMapType(x *ast.MapType) Type {
func (a *typeCompiler) compileType(x ast.Expr, allowRec bool) Type {
switch x := x.(type) {
case *ast.BadExpr:
// Error already reported by parser
a.silentErrors++;
return nil;
case *ast.Ident: