// 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;
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
*/
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) {
// 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:
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;
}
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;
}
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;
}
goto notimpl;
case *ast.UnaryExpr:
- v := a.compile(x.X);
+ v := a.compile(x.X, false);
if v == nil {
return nil;
}
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);
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 {
}
return a.compileVariable(level, def);
case Type:
+ if callCtx {
+ return a.exprFromType(def);
+ }
a.diag("type %v used as expression", name);
return nil;
}
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;
// 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;
}
}
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});
// 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);
};
}
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
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:
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");
}
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;;
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
return tI.(*FuncType);
}
- t := &FuncType{commonType{}, in, variadic, out};
+ t := &FuncType{commonType{}, in, variadic, out, ""};
outMap.Put(out, t);
return t;
}
}
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 {
}
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}};
}
}
func (t *MapType) Zero() Value {
+ // The value of an uninitialized map is nil.
return &mapV{nil};
}
}
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);
+}