1
0
mirror of https://github.com/golang/go synced 2024-10-04 16:21:22 -06:00
go/usr/austin/eval/stmt.go
Austin Clements 75760a4b5d Implement multi-valued functions, multi-valued return, and
unpacking for assignments, call arguments, and returns.  This
change revamps the whole assignment compilation system to be
multi-valued, using the new MultiType type and multiV value.
Function calls, returns, and assignments now share a lot of
code and produce very consistent error messages.

R=rsc
APPROVED=rsc
DELTA=510  (335 added, 74 deleted, 101 changed)
OCL=32248
CL=32258
2009-07-27 17:32:35 -07:00

515 lines
11 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 (
"eval";
"log";
"os";
"go/ast";
"go/scanner";
"go/token";
"strconv";
)
/*
* Statement compiler
*/
type stmtCompiler struct {
*blockCompiler;
pos token.Position;
// err should be initialized to true before visiting and set
// to false when the statement is compiled successfully. The
// function invoking Visit should or this with
// blockCompiler.err. This is less error prone than setting
// blockCompiler.err on every failure path.
err bool;
}
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) {
a.err = false;
}
func (a *stmtCompiler) DoLabeledStmt(s *ast.LabeledStmt) {
log.Crash("Not implemented");
}
func (a *stmtCompiler) DoExprStmt(s *ast.ExprStmt) {
// TODO(austin) Permit any 0 or more valued function call
e := a.compileExpr(a.scope, s.X, false);
if e == nil {
return;
}
if e.exec == nil {
a.diag("%s cannot be used as expression statement", e.desc);
return;
}
exec := e.exec;
a.push(func(v *vm) {
exec(v.f);
});
a.err = false;
}
func (a *stmtCompiler) DoIncDecStmt(s *ast.IncDecStmt) {
log.Crash("Not implemented");
}
func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
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] = a.compileExpr(a.scope, re, false);
if rs[i] == nil {
bad = true;
continue;
}
}
errOp := "assignment";
if s.Tok == token.DEFINE {
errOp = "definition";
}
ac, ok := a.checkAssign(s.Pos(), rs, "assignment", "value");
if !ok {
bad = true;
}
// If this is a definition and the LHS is too big, we won't be
// able to produce the usual error message because we can't
// begin to infer the types of the LHS.
if s.Tok == token.DEFINE && len(s.Lhs) > len(ac.rmt.Elems) {
a.diag("not enough values for definition");
bad = true;
}
// Compile left side
ls := make([]*exprCompiler, len(s.Lhs));
nDefs := 0;
for i, le := range s.Lhs {
if s.Tok == token.DEFINE {
// Check that it's an identifier
ident, ok := le.(*ast.Ident);
if !ok {
a.diagAt(le, "left side of := must be a name");
bad = true;
// Suppress new defitions errors
nDefs++;
continue;
}
// Is this simply an assignment?
if _, ok := a.scope.defs[ident.Value]; ok {
goto assignment;
}
nDefs++;
// Compute the identifier's type from the RHS
// type. We use the computed MultiType so we
// don't have to worry about unpacking.
var lt Type;
switch {
case i >= len(ac.rmt.Elems):
// Define a placeholder. We already
// gave the "not enough" error above.
lt = nil;
case ac.rmt.Elems[i] == nil:
// We gave the error when we compiled
// the RHS.
lt = nil;
case ac.rmt.Elems[i].isIdeal():
// 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.
switch {
case ac.rmt.Elems[i].isInteger():
lt = IntType;
case ac.rmt.Elems[i].isFloat():
lt = FloatType;
default:
log.Crashf("unexpected ideal type %v", rs[i].t);
}
default:
lt = ac.rmt.Elems[i];
}
// Define identifier
v := a.scope.DefineVar(ident.Value, lt);
if v == nil {
log.Crashf("Failed to define %s", ident.Value);
}
}
assignment:
ls[i] = a.compileExpr(a.scope, le, false);
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;
}
}
// 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;
}
if bad {
return;
}
// Create assigner
var lt Type;
n := len(s.Lhs);
if n == 1 {
lt = ls[0].t;
} else {
lts := make([]Type, len(ls));
for i, l := range ls {
if l != nil {
lts[i] = l.t;
}
}
lt = NewMultiType(lts);
}
assign := ac.compile(lt);
if assign == nil {
return;
}
// Compile
if n == 1 {
// Don't need temporaries and can avoid []Value.
lf := ls[0].evalAddr;
a.push(func(v *vm) { assign(lf(v.f), v.f) });
} else if s.Tok == token.DEFINE && nDefs == n {
// Don't need temporaries
lfs := make([]func(*Frame) Value, n);
for i, l := range ls {
lfs[i] = l.evalAddr;
}
a.push(func(v *vm) {
dest := make([]Value, n);
for i, lf := range lfs {
dest[i] = lf(v.f);
}
assign(multiV(dest), v.f);
});
} else {
// Need temporaries
lmt := lt.(*MultiType);
lfs := make([]func(*Frame) Value, n);
for i, l := range ls {
lfs[i] = l.evalAddr;
}
a.push(func(v *vm) {
temp := lmt.Zero().(multiV);
assign(temp, v.f);
// Copy to destination
for i := 0; i < n; i ++ {
// TODO(austin) Need to evaluate LHS
// before RHS
lfs[i](v.f).Assign(temp[i]);
}
});
}
a.err = false;
}
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 := a.compileExpr(a.scope, s.Lhs[0], false);
r := a.compileExpr(a.scope, s.Rhs[0], false);
if l == nil || r == nil {
return;
}
if l.evalAddr == nil {
l.diag("cannot assign to %s", l.desc);
return;
}
effect, l := l.extractEffect();
binop := r.copy();
binop.pos = s.TokPos;
binop.doBinaryExpr(assignOpToOp[s.Tok], l, r);
if binop.t == nil {
return;
}
assign := a.compileAssign(s.Pos(), l.t, []*exprCompiler{binop}, "assignment", "value");
if assign == nil {
log.Crashf("compileAssign type check failed");
}
lf := l.evalAddr;
a.push(func(v *vm) {
effect(v.f);
assign(lf(v.f), v.f);
});
a.err = false;
}
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) {
// Supress return errors even if we fail to compile this
// return statement.
a.returned = true;
if len(s.Results) == 0 && (len(a.fnType.Out) == 0 || a.outVarsNamed) {
// Simple case. Simply exit from the function.
a.push(func(v *vm) { v.pc = ^uint(0) });
a.err = false;
return;
}
// Compile expressions
bad := false;
rs := make([]*exprCompiler, len(s.Results));
for i, re := range s.Results {
rs[i] = a.compileExpr(a.scope, re, false);
if rs[i] == nil {
bad = true;
}
}
if bad {
return;
}
// Create assigner
// However, if the expression list in the "return" statement
// is a single call to a multi-valued function, the values
// returned from the called function will be returned from
// this one.
assign := a.compileAssign(s.Pos(), NewMultiType(a.fnType.Out), rs, "return", "value");
if assign == nil {
return;
}
// XXX(Spec) "The result types of the current function and the
// called function must match." Match is fuzzy. It should
// say that they must be assignment compatible.
// Compile
start := len(a.fnType.In);
nout := len(a.fnType.Out);
a.push(func(v *vm) {
assign(multiV(v.activation.Vars[start:start+nout]), v.f);
v.pc = ^uint(0);
});
a.err = false;
}
func (a *stmtCompiler) DoBranchStmt(s *ast.BranchStmt) {
log.Crash("Not implemented");
}
func (a *stmtCompiler) DoBlockStmt(s *ast.BlockStmt) {
blockScope := a.scope.Fork();
bc := &blockCompiler{a.funcCompiler, blockScope, false};
a.push(func(v *vm) {
v.f = blockScope.NewFrame(v.f);
});
bc.compileBlock(s);
a.push(func(v *vm) {
v.f = v.f.Outer;
});
a.returned = a.returned || bc.returned;
a.err = false;
}
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");
}
func (a *blockCompiler) compileBlock(block *ast.BlockStmt) {
for i, sub := range block.List {
sc := &stmtCompiler{a, sub.Pos(), true};
sub.Visit(sc);
a.err = a.err || sc.err;
}
}
func (a *compiler) compileFunc(scope *Scope, decl *FuncDecl, body *ast.BlockStmt) (func (f *Frame) Func) {
// Create body scope
//
// The scope of a parameter or result is the body of the
// corresponding function.
bodyScope := scope.Fork();
for i, t := range decl.Type.In {
bodyScope.DefineVar(decl.InNames[i].Value, t);
}
for i, t := range decl.Type.Out {
if decl.OutNames[i] != nil {
bodyScope.DefineVar(decl.OutNames[i].Value, t);
} else {
// TODO(austin) Not technically a temp
bodyScope.DefineTemp(t);
}
}
// Create block context
fc := &funcCompiler{a, decl.Type, false, newCodeBuf(), false};
if len(decl.OutNames) > 0 && decl.OutNames[0] != nil {
fc.outVarsNamed = true;
}
bc := &blockCompiler{fc, bodyScope, false};
// Compile body
bc.compileBlock(body);
if fc.err {
return nil;
}
// TODO(austin) Check that all gotos were linked?
// Check that the body returned if necessary
if len(decl.Type.Out) > 0 && !bc.returned {
// XXX(Spec) Not specified.
a.diagAt(&body.Rbrace, "function ends without a return statement");
return nil;
}
code := fc.get();
return func(f *Frame) Func { return &evalFunc{bodyScope, f, code} };
}
/*
* Testing interface
*/
type Stmt struct {
f func (f *Frame);
}
func (s *Stmt) Exec(f *Frame) {
s.f(f);
}
func CompileStmt(scope *Scope, stmt ast.Stmt) (*Stmt, os.Error) {
errors := scanner.NewErrorVector();
cc := &compiler{errors};
fc := &funcCompiler{cc, nil, false, newCodeBuf(), false};
bc := &blockCompiler{fc, scope, false};
sc := &stmtCompiler{bc, stmt.Pos(), true};
stmt.Visit(sc);
fc.err = fc.err || sc.err;
if fc.err {
return nil, errors.GetError(scanner.Sorted);
}
code := fc.get();
return &Stmt{func(f *Frame) { code.exec(f); }}, nil;
}