From: Austin Clements Date: Tue, 21 Jul 2009 22:40:41 +0000 (-0700) Subject: Implement assignment. Move convertTo. X-Git-Tag: weekly.2009-11-06~1080 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d426b6389e676707af652f96d8929a3c04c8809a;p=gostls13.git Implement assignment. Move convertTo. R=rsc APPROVED=rsc DELTA=591 (497 added, 76 deleted, 18 changed) OCL=31933 CL=31955 --- diff --git a/usr/austin/eval/expr.go b/usr/austin/eval/expr.go index 6c8c9c819e..c63ca76895 100644 --- a/usr/austin/eval/expr.go +++ b/usr/austin/eval/expr.go @@ -79,6 +79,7 @@ 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 genAssign(lt Type, r *exprCompiler) (func(lv Value, f *Frame)) func (a *exprCompiler) copy() *exprCompiler { ec := newExprCompiler(a.exprContext, a.pos); @@ -104,6 +105,11 @@ 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); } +/* + * "As" functions. These retrieve evaluator functions from an + * exprCompiler, panicking if the requested evaluator is nil. + */ + func (a *exprCompiler) asBool() (func(f *Frame) bool) { if a.evalBool == nil { log.Crashf("tried to get %v node as boolType", a.t); @@ -167,8 +173,149 @@ func (a *exprCompiler) asPtr() (func(f *Frame) Value) { return a.evalPtr; } -// TODO(austin) Move convertTo somewhere more reasonable -func (a *exprCompiler) convertTo(t Type) *exprCompiler +/* + * Common expression manipulations + */ + +// 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; + + // XXX(Spec) The spec says "It is erroneous". + // + // It is an error 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 := a.copy(); + res.t = t; + 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; +} + +// mkAssign takes an optional expected l-value type, lt, and an +// r-value expression compiler, r, and returns the expected l-value +// type and a function that evaluates the r-value and assigns it to +// the l-value lv. +// +// If lt is non-nil, the returned l-value type will always be lt. If +// lt is nil, mkAssign will infer and return the appropriate l-value +// type, or produce an error. +// +// errOp specifies the operation name to use for error messages, such +// as "assignment", or "function call". errPos, if non-zero, +// specifies the position of this assignment (for tuple assignments or +// function arguments). errPosName specifies the name to use for +// positions. +// +// If the assignment fails to typecheck, this generates an error +// message and returns nil, nil. +func mkAssign(lt Type, r *exprCompiler, errOp string, errPos int, errPosName string) (Type, func(lv Value, f *Frame)) { + // However, when [an ideal is] (used in an expression) + // assigned to a variable or typed constant, the destination + // must be able to represent the assigned value. + if r.t.isIdeal() && (lt == nil || lt.isInteger() || lt.isFloat()) { + // If the type is absent and the corresponding + // expression is a constant expression of ideal + // integer or ideal float type, the type of the + // declared variable is int or float respectively. + if lt == nil { + switch { + case r.t.isInteger(): + lt = IntType; + case r.t.isFloat(): + lt = FloatType; + default: + log.Crashf("unexpected ideal type %v", r.t); + } + } + r = r.convertTo(lt); + if r == nil { + return nil, nil; + } + } + + // TOOD(austin) Deal with assignment special cases + + if lt == nil { + lt = r.t; + } else { + // Values of any type may always be assigned to + // variables of compatible static type. + if !lt.compatible(r.t) { + if errPos == 0 { + r.diag("illegal operand types for %s\n\t%v\n\t%v", errOp, lt, r.t); + } else { + r.diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", errPosName, errPos, errOp, lt, r.t); + } + return nil, nil; + } + } + + // Compile + return lt, genAssign(lt, r); +} + +/* + * Expression visitors + */ func (a *exprCompiler) DoBadExpr(x *ast.BadExpr) { // Do nothing. Already reported by parser. @@ -506,85 +653,9 @@ func (a *exprCompiler) DoUnaryExpr(x *ast.UnaryExpr) { } } -// 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 := a.copy(); - res.t = t; - 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; -} - var binOpDescs = make(map[token.Token] string) -func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { - l, r := a.copyVisit(x.X), a.copyVisit(x.Y); - if l.t == nil || r.t == nil { - return; - } - +func (a *exprCompiler) doBinaryExpr(op token.Token, l, r *exprCompiler) { // Save the original types of l.t and r.t for error messages. origlt := l.t; origrt := r.t; @@ -602,7 +673,7 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { // relevant only for / and %? If I add an ideal int and an // ideal float, I get an ideal float. - if x.Op != token.SHL && x.Op != token.SHR { + if op != token.SHL && op != token.SHR { // 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 @@ -653,24 +724,24 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { }; // Type check - switch x.Op { + switch op { case token.ADD: if !compat() || (!integers() && !floats() && !strings()) { - a.diagOpTypes(x.Op, origlt, origrt); + a.diagOpTypes(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); + a.diagOpTypes(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); + a.diagOpTypes(op, origlt, origrt); return; } a.t = l.t; @@ -684,7 +755,7 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { // (§Arithmetic operators)" suggests so and 6g agrees. if !l.t.isInteger() || !(r.t.isInteger() || r.t.isIdeal()) { - a.diagOpTypes(x.Op, origlt, origrt); + a.diagOpTypes(op, origlt, origrt); return; } @@ -759,7 +830,7 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { // ... booleans may be compared only for equality or // inequality. if l.t.literal() == BoolType || r.t.literal() == BoolType { - a.diagOpTypes(x.Op, origlt, origrt); + a.diagOpTypes(op, origlt, origrt); return; } @@ -783,23 +854,23 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { // "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); + log.Crashf("Binary op %v not implemented", op); // TODO(austin) Unnamed bool? Named bool? a.t = BoolType; default: - log.Crashf("unknown binary operator %v", x.Op); + log.Crashf("unknown binary operator %v", op); } var ok bool; - a.desc, ok = binOpDescs[x.Op]; + a.desc, ok = binOpDescs[op]; if !ok { - a.desc = x.Op.String() + " expression"; - binOpDescs[x.Op] = a.desc; + a.desc = op.String() + " expression"; + binOpDescs[op] = a.desc; } // Compile - switch x.Op { + switch op { case token.ADD: a.genBinOpAdd(l, r); @@ -860,8 +931,17 @@ func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { } default: - log.Crashf("Compilation of binary op %v not implemented", x.Op); + log.Crashf("Compilation of binary op %v not implemented", op); + } +} + +func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) { + l, r := a.copyVisit(x.X), a.copyVisit(x.Y); + if l.t == nil || r.t == nil { + return; } + + a.doBinaryExpr(x.Op, l, r); } func (a *exprCompiler) DoKeyValueExpr(x *ast.KeyValueExpr) { @@ -1320,3 +1400,32 @@ func (a *exprCompiler) genBinOpShr(l *exprCompiler, r *exprCompiler) { log.Crashf("unexpected result type %v at %v", l.t.literal(), a.pos); } } + +func genAssign(lt Type, r *exprCompiler) (func(lv Value, f *Frame)) { + switch _ := lt.literal().(type) { + case *boolType: + rf := r.asBool(); + return func(lv Value, f *Frame) { lv.(BoolValue).Set(rf(f)) }; + case *uintType: + rf := r.asUint(); + return func(lv Value, f *Frame) { lv.(UintValue).Set(rf(f)) }; + case *intType: + rf := r.asInt(); + return func(lv Value, f *Frame) { lv.(IntValue).Set(rf(f)) }; + case *floatType: + rf := r.asFloat(); + return func(lv Value, f *Frame) { lv.(FloatValue).Set(rf(f)) }; + case *stringType: + rf := r.asString(); + return func(lv Value, f *Frame) { lv.(StringValue).Set(rf(f)) }; + case *ArrayType: + rf := r.asArray(); + return func(lv Value, f *Frame) { lv.Assign(rf(f)) }; + case *PtrType: + rf := r.asPtr(); + return func(lv Value, f *Frame) { lv.(PtrValue).Set(rf(f)) }; + default: + log.Crashf("unexpected left operand type %v at %v", lt.literal(), r.pos); + } + panic(); +} diff --git a/usr/austin/eval/stmt.go b/usr/austin/eval/stmt.go new file mode 100644 index 0000000000..4b20c8c02e --- /dev/null +++ b/usr/austin/eval/stmt.go @@ -0,0 +1,316 @@ +// 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 ( + "eval"; + "fmt"; + "log"; + "os"; + "go/ast"; + "go/scanner"; + "go/token"; +) + +type stmtCompiler struct { + scope *Scope; + errors scanner.ErrorHandler; + pos token.Position; + f func (f *Frame); +} + +func (a *stmtCompiler) diagAt(pos token.Position, format string, args ...) { + a.errors.Error(pos, fmt.Sprintf(format, args)); +} + +func (a *stmtCompiler) diag(format string, args ...) { + a.diagAt(a.pos, format, args); +} + +/* + * Statement visitors + */ + +func (a *stmtCompiler) DoBadStmt(s *ast.BadStmt) { + // Do nothing. Already reported by parser. +} + +func (a *stmtCompiler) DoDeclStmt(s *ast.DeclStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoEmptyStmt(s *ast.EmptyStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoLabeledStmt(s *ast.LabeledStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoExprStmt(s *ast.ExprStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoIncDecStmt(s *ast.IncDecStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) doAssign(s *ast.AssignStmt) { + if len(s.Lhs) != len(s.Rhs) { + log.Crashf("Unbalanced assignment not implemented %v %v %v", len(s.Lhs), s.Tok, len(s.Rhs)); + } + + bad := false; + + // Compile right side first so we have the types when + // compiling the left side and so we don't see definitions + // made on the left side. + rs := make([]*exprCompiler, len(s.Rhs)); + for i, re := range s.Rhs { + rs[i] = compileExpr(re, a.scope, a.errors); + if rs[i] == nil { + bad = true; + } + } + + // Compile left side and generate assigners + ls := make([]*exprCompiler, len(s.Lhs)); + as := make([]func(lv Value, f *Frame), len(s.Lhs)); + nDefs := 0; + for i, le := range s.Lhs { + errPos := i + 1; + if len(s.Lhs) == 1 { + errPos = 0; + } + + if s.Tok == token.DEFINE { + // Check that it's an identifier + ident, ok := le.(*ast.Ident); + if !ok { + a.diagAt(le.Pos(), "left side of := must be a name"); + bad = true; + continue; + } + + // Is this simply an assignment? + if _, ok := a.scope.defs[ident.Value]; ok { + goto assignment; + } + + if rs[i] == nil { + // TODO(austin) Define a placeholder. + continue; + } + + // Generate assigner and get type + var lt Type; + lt, as[i] = mkAssign(nil, rs[i], "assignment", errPos, "position"); + if lt == nil { + bad = true; + continue; + } + + // Define identifier + v := a.scope.DefineVar(ident.Value, lt); + nDefs++; + if v == nil { + log.Crashf("Failed to define %s", ident.Value); + } + } + + assignment: + ls[i] = compileExpr(le, a.scope, a.errors); + if ls[i] == nil { + bad = true; + continue; + } + + if ls[i].evalAddr == nil { + ls[i].diag("cannot assign to %s", ls[i].desc); + bad = true; + continue; + } + + // Generate assigner + if as[i] == nil { + var lt Type; + lt, as[i] = mkAssign(ls[i].t, rs[i], "assignment", errPos, "position"); + if lt == nil { + bad = true; + continue; + } + } + } + + if bad { + return; + } + + + // A short variable declaration may redeclare variables + // provided they were originally declared in the same block + // with the same type, and at least one of the variables is + // new. + if s.Tok == token.DEFINE && nDefs == 0 { + a.diag("at least one new variable must be declared"); + return; + } + + n := len(s.Lhs); + if n == 1 { + lf := ls[0].evalAddr; + assign := as[0]; + a.f = func(f *Frame) { assign(lf(f), f) }; + } else { + a.f = func(f *Frame) { + temps := make([]Value, n); + // Assign to temporaries + for i := 0; i < n; i++ { + // TODO(austin) Don't capture ls + temps[i] = ls[i].t.Zero(); + as[i](temps[i], f); + } + // Copy to destination + for i := 0; i < n; i++ { + ls[i].evalAddr(f).Assign(temps[i]); + } + } + } +} + +var assignOpToOp = map[token.Token] token.Token { + token.ADD_ASSIGN : token.ADD, + token.SUB_ASSIGN : token.SUB, + token.MUL_ASSIGN : token.MUL, + token.QUO_ASSIGN : token.QUO, + token.REM_ASSIGN : token.REM, + + token.AND_ASSIGN : token.AND, + token.OR_ASSIGN : token.OR, + token.XOR_ASSIGN : token.XOR, + token.SHL_ASSIGN : token.SHL, + token.SHR_ASSIGN : token.SHR, + token.AND_NOT_ASSIGN : token.AND_NOT, +} + +func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) { + if len(s.Lhs) != 1 || len(s.Rhs) != 1 { + a.diag("tuple assignment cannot be combined with an arithmetic operation"); + return; + } + + l := compileExpr(s.Lhs[0], a.scope, a.errors); + r := compileExpr(s.Rhs[0], a.scope, a.errors); + if l == nil || r == nil { + return; + } + + if l.evalAddr == nil { + l.diag("cannot assign to %s", l.desc); + return; + } + + ec := r.copy(); + ec.pos = s.TokPos; + ec.doBinaryExpr(assignOpToOp[s.Tok], l, r); + if ec.t == nil { + return; + } + + lf := l.evalAddr; + _, assign := mkAssign(l.t, r, "assignment", 0, ""); + if assign == nil { + return; + } + a.f = func(f *Frame) { assign(lf(f), f) }; +} + +func (a *stmtCompiler) DoAssignStmt(s *ast.AssignStmt) { + switch s.Tok { + case token.ASSIGN, token.DEFINE: + a.doAssign(s); + + default: + a.doAssignOp(s); + } +} + +func (a *stmtCompiler) DoGoStmt(s *ast.GoStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoDeferStmt(s *ast.DeferStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoBranchStmt(s *ast.BranchStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoBlockStmt(s *ast.BlockStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoIfStmt(s *ast.IfStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoCaseClause(s *ast.CaseClause) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoTypeCaseClause(s *ast.TypeCaseClause) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoTypeSwitchStmt(s *ast.TypeSwitchStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoCommClause(s *ast.CommClause) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoSelectStmt(s *ast.SelectStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoForStmt(s *ast.ForStmt) { + log.Crash("Not implemented"); +} + +func (a *stmtCompiler) DoRangeStmt(s *ast.RangeStmt) { + log.Crash("Not implemented"); +} + +/* + * Public interface + */ + +type Stmt struct { + f func (f *Frame); +} + +func (s *Stmt) Exec(f *Frame) { + s.f(f); +} + +func CompileStmt(stmt ast.Stmt, scope *Scope) (*Stmt, os.Error) { + errors := scanner.NewErrorVector(); + sc := &stmtCompiler{scope, errors, stmt.Pos(), nil}; + stmt.Visit(sc); + if sc.f == nil { + return nil, errors.GetError(scanner.Sorted); + } + return &Stmt{sc.f}, nil; +}