mirror of https://github.com/golang/go.git
Implement assignment. Move convertTo.
R=rsc APPROVED=rsc DELTA=591 (497 added, 76 deleted, 18 changed) OCL=31933 CL=31955
This commit is contained in:
parent
3d42e691b9
commit
d426b6389e
|
|
@ -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,10 +931,19 @@ 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) {
|
||||
log.Crash("Not implemented");
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue