1
0
mirror of https://github.com/golang/go synced 2024-11-13 19:20:31 -07:00

Make the statement compiler not use the AST visitor.

In the process, I made error handling in the statement
compiler much saner.  Instead of separately tracking various
error flags with weird relations, I just track if any error
messages have been produced.

R=rsc
APPROVED=rsc
DELTA=308  (98 added, 135 deleted, 75 changed)
OCL=33870
CL=33961
This commit is contained in:
Austin Clements 2009-08-27 11:21:52 -07:00
parent 4b409289f6
commit c0dd9f526b
3 changed files with 149 additions and 186 deletions

View File

@ -24,13 +24,21 @@ type positioned interface {
// case it should be package compiler. // case it should be package compiler.
type compiler struct { type compiler struct {
errors scanner.ErrorHandler; errors scanner.ErrorHandler;
numErrors int;
silentErrors int;
} }
func (a *compiler) diagAt(pos positioned, format string, args ...) { func (a *compiler) diagAt(pos positioned, format string, args ...) {
a.errors.Error(pos.Pos(), fmt.Sprintf(format, args)); a.errors.Error(pos.Pos(), fmt.Sprintf(format, args));
a.numErrors++;
}
func (a *compiler) numError() int {
return a.numErrors + a.silentErrors;
} }
// TODO(austin) These can all go in stmt.go now
type label struct { type label struct {
name string; name string;
desc string; desc string;
@ -61,7 +69,6 @@ type funcCompiler struct {
*codeBuf; *codeBuf;
flow *flowBuf; flow *flowBuf;
labels map[string] *label; labels map[string] *label;
err bool;
} }
// A blockCompiler captures information used throughout the compilation // A blockCompiler captures information used throughout the compilation

View File

@ -615,6 +615,7 @@ func (a *exprCompiler) compile(x ast.Expr) *expr {
// Remaining expressions // Remaining expressions
case *ast.BadExpr: case *ast.BadExpr:
// Error already reported by parser // Error already reported by parser
a.silentErrors++;
return nil; return nil;
case *ast.BinaryExpr: case *ast.BinaryExpr:
@ -740,6 +741,7 @@ func (a *exprInfo) compileIdent(b *block, constant bool, name string) *expr {
func (a *exprInfo) compileVariable(level int, v *Variable) *expr { func (a *exprInfo) compileVariable(level int, v *Variable) *expr {
if v.Type == nil { if v.Type == nil {
// Placeholder definition from an earlier error // Placeholder definition from an earlier error
a.silentErrors++;
return nil; return nil;
} }
expr := a.newExpr(v.Type, "variable"); expr := a.newExpr(v.Type, "variable");
@ -1614,7 +1616,12 @@ func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) {
func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr { func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr {
ec := &exprCompiler{a, b, constant}; ec := &exprCompiler{a, b, constant};
return ec.compile(expr); nerr := a.numError();
e := ec.compile(expr);
if e == nil && nerr == a.numError() {
log.Crashf("expression compilation failed without reporting errors");
}
return e;
} }
// extractEffect separates out any effects that the expression may // extractEffect separates out any effects that the expression may
@ -1698,7 +1705,7 @@ func (expr *Expr) Eval(f *Frame) Value {
func CompileExpr(scope *Scope, expr ast.Expr) (*Expr, os.Error) { func CompileExpr(scope *Scope, expr ast.Expr) (*Expr, os.Error) {
errors := scanner.NewErrorVector(); errors := scanner.NewErrorVector();
cc := &compiler{errors}; cc := &compiler{errors, 0, 0};
ec := cc.compileExpr(scope.block, false, expr); ec := cc.compileExpr(scope.block, false, expr);
if ec == nil { if ec == nil {

View File

@ -28,12 +28,6 @@ type stmtCompiler struct {
pos token.Position; pos token.Position;
// This statement's label, or nil if it is not labeled. // This statement's label, or nil if it is not labeled.
stmtLabel *label; stmtLabel *label;
// 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 ...) { func (a *stmtCompiler) diag(format string, args ...) {
@ -187,7 +181,7 @@ func (f *flowBuf) reachesEnd(pc uint) bool {
// gotosObeyScopes returns true if no goto statement causes any // gotosObeyScopes returns true if no goto statement causes any
// variables to come into scope that were not in scope at the point of // variables to come into scope that were not in scope at the point of
// the goto. Reports any errors using the given compiler. // the goto. Reports any errors using the given compiler.
func (f *flowBuf) gotosObeyScopes(a *compiler) bool { func (f *flowBuf) gotosObeyScopes(a *compiler) {
for pos, src := range f.gotos { for pos, src := range f.gotos {
tgt := f.labels[src.target]; tgt := f.labels[src.target];
@ -201,7 +195,7 @@ func (f *flowBuf) gotosObeyScopes(a *compiler) bool {
if b != tgt.block { if b != tgt.block {
// We jumped into a deeper block // We jumped into a deeper block
a.diagAt(pos, "goto causes variables to come into scope"); a.diagAt(pos, "goto causes variables to come into scope");
return false; return;
} }
// There must be no variables in the target block that // There must be no variables in the target block that
@ -210,11 +204,10 @@ func (f *flowBuf) gotosObeyScopes(a *compiler) bool {
for i := range numVars { for i := range numVars {
if tgtNumVars[i] > numVars[i] { if tgtNumVars[i] > numVars[i] {
a.diagAt(pos, "goto causes variables to come into scope"); a.diagAt(pos, "goto causes variables to come into scope");
return false; return;
} }
} }
} }
return true;
} }
/* /*
@ -246,20 +239,98 @@ func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable {
// TODO(austin) Move doAssign to here // TODO(austin) Move doAssign to here
/* /*
* Statement visitors * Statement compiler
*/ */
func (a *stmtCompiler) DoBadStmt(s *ast.BadStmt) { func (a *stmtCompiler) compile(s ast.Stmt) {
// Do nothing. Already reported by parser. if a.block.inner != nil {
log.Crash("Child scope still entered");
}
notimpl := false;
switch s := s.(type) {
case *ast.BadStmt:
// Error already reported by parser.
a.silentErrors++;
case *ast.DeclStmt:
a.compileDeclStmt(s);
case *ast.EmptyStmt:
// Do nothing.
case *ast.LabeledStmt:
a.compileLabeledStmt(s);
case *ast.ExprStmt:
a.compileExprStmt(s);
case *ast.IncDecStmt:
a.compileIncDecStmt(s);
case *ast.AssignStmt:
a.compileAssignStmt(s);
case *ast.GoStmt:
notimpl = true;
case *ast.DeferStmt:
notimpl = true;
case *ast.ReturnStmt:
a.compileReturnStmt(s);
case *ast.BranchStmt:
a.compileBranchStmt(s);
case *ast.BlockStmt:
a.compileBlockStmt(s);
case *ast.IfStmt:
a.compileIfStmt(s);
case *ast.CaseClause:
a.diag("case clause outside switch");
case *ast.SwitchStmt:
a.compileSwitchStmt(s);
case *ast.TypeCaseClause:
notimpl = true;
case *ast.TypeSwitchStmt:
notimpl = true;
case *ast.CommClause:
notimpl = true;
case *ast.SelectStmt:
notimpl = true;
case *ast.ForStmt:
a.compileForStmt(s);
case *ast.RangeStmt:
notimpl = true;
default:
log.Crashf("unexpected ast node type %T", s);
}
if notimpl {
a.diag("%T statment node not implemented", s);
}
if a.block.inner != nil {
log.Crash("Forgot to exit child scope");
}
} }
func (a *stmtCompiler) DoDeclStmt(s *ast.DeclStmt) { func (a *stmtCompiler) compileDeclStmt(s *ast.DeclStmt) {
ok := true;
switch decl := s.Decl.(type) { switch decl := s.Decl.(type) {
case *ast.BadDecl: case *ast.BadDecl:
// Do nothing. Already reported by parser. // Do nothing. Already reported by parser.
ok = false; a.silentErrors++;
case *ast.FuncDecl: case *ast.FuncDecl:
log.Crash("FuncDecl at statement level"); log.Crash("FuncDecl at statement level");
@ -273,7 +344,7 @@ func (a *stmtCompiler) DoDeclStmt(s *ast.DeclStmt) {
log.Crashf("%v not implemented", decl.Tok); log.Crashf("%v not implemented", decl.Tok);
case token.TYPE: case token.TYPE:
ok = a.compileTypeDecl(a.block, decl); a.compileTypeDecl(a.block, decl);
case token.VAR: case token.VAR:
for _, spec := range decl.Specs { for _, spec := range decl.Specs {
@ -285,14 +356,9 @@ func (a *stmtCompiler) DoDeclStmt(s *ast.DeclStmt) {
log.Crash("Type and Values nil"); log.Crash("Type and Values nil");
} }
t := a.compileType(a.block, spec.Type); t := a.compileType(a.block, spec.Type);
if t == nil { // Define placeholders even if type compile failed
// Define placeholders
ok = false;
}
for _, n := range spec.Names { for _, n := range spec.Names {
if a.defineVar(n, t) == nil { a.defineVar(n, t);
ok = false;
}
} }
} else { } else {
// Decalaration with assignment // Decalaration with assignment
@ -301,36 +367,21 @@ func (a *stmtCompiler) DoDeclStmt(s *ast.DeclStmt) {
lhs[i] = n; lhs[i] = n;
} }
a.doAssign(lhs, spec.Values, decl.Tok, spec.Type); a.doAssign(lhs, spec.Values, decl.Tok, spec.Type);
// TODO(austin) This is ridiculous. doAssign
// indicates failure by setting a.err.
if a.err {
ok = false;
}
} }
} }
} }
default: default:
log.Crashf("Unexpected Decl type %T", s.Decl); log.Crashf("Unexpected Decl type %T", s.Decl);
} }
if ok {
a.err = false;
}
} }
func (a *stmtCompiler) DoEmptyStmt(s *ast.EmptyStmt) { func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) {
a.err = false;
}
func (a *stmtCompiler) DoLabeledStmt(s *ast.LabeledStmt) {
bad := false;
// Define label // Define label
l, ok := a.labels[s.Label.Value]; l, ok := a.labels[s.Label.Value];
if ok { if ok {
if l.resolved.IsValid() { if l.resolved.IsValid() {
a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Value, &l.resolved); a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Value, &l.resolved);
bad = true;
} }
} else { } else {
pc := badPC; pc := badPC;
@ -347,15 +398,11 @@ func (a *stmtCompiler) DoLabeledStmt(s *ast.LabeledStmt) {
a.flow.putLabel(l.name, a.block); a.flow.putLabel(l.name, a.block);
// Compile the statement. Reuse our stmtCompiler for simplicity. // Compile the statement. Reuse our stmtCompiler for simplicity.
a.pos = s.Stmt.Pos(); sc := &stmtCompiler{a.blockCompiler, s.Stmt.Pos(), l};
a.stmtLabel = l; sc.compile(s.Stmt);
s.Stmt.Visit(a);
if bad {
a.err = true;
}
} }
func (a *stmtCompiler) DoExprStmt(s *ast.ExprStmt) { func (a *stmtCompiler) compileExprStmt(s *ast.ExprStmt) {
bc := a.enterChild(); bc := a.enterChild();
defer bc.exit(); defer bc.exit();
@ -373,10 +420,9 @@ func (a *stmtCompiler) DoExprStmt(s *ast.ExprStmt) {
a.push(func(v *vm) { a.push(func(v *vm) {
exec(v.f); exec(v.f);
}); });
a.err = false;
} }
func (a *stmtCompiler) DoIncDecStmt(s *ast.IncDecStmt) { func (a *stmtCompiler) compileIncDecStmt(s *ast.IncDecStmt) {
// Create temporary block for extractEffect // Create temporary block for extractEffect
bc := a.enterChild(); bc := a.enterChild();
defer bc.exit(); defer bc.exit();
@ -429,11 +475,10 @@ func (a *stmtCompiler) DoIncDecStmt(s *ast.IncDecStmt) {
effect(v.f); effect(v.f);
assign(lf(v.f), v.f); assign(lf(v.f), v.f);
}); });
a.err = false;
} }
func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, declTypeExpr ast.Expr) { func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, declTypeExpr ast.Expr) {
bad := false; nerr := a.numError();
// Compile right side first so we have the types when // Compile right side first so we have the types when
// compiling the left side and so we don't see definitions // compiling the left side and so we don't see definitions
@ -441,9 +486,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
rs := make([]*expr, len(rhs)); rs := make([]*expr, len(rhs));
for i, re := range rhs { for i, re := range rhs {
rs[i] = a.compileExpr(a.block, false, re); rs[i] = a.compileExpr(a.block, false, re);
if rs[i] == nil {
bad = true;
}
} }
errOp := "assignment"; errOp := "assignment";
@ -451,9 +493,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
errOp = "declaration"; errOp = "declaration";
} }
ac, ok := a.checkAssign(a.pos, rs, errOp, "value"); ac, ok := a.checkAssign(a.pos, rs, errOp, "value");
if !ok {
bad = true;
}
ac.allowMapForms(len(lhs)); ac.allowMapForms(len(lhs));
// If this is a definition and the LHS is too big, we won't be // If this is a definition and the LHS is too big, we won't be
@ -461,16 +500,12 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
// begin to infer the types of the LHS. // begin to infer the types of the LHS.
if (tok == token.DEFINE || tok == token.VAR) && len(lhs) > len(ac.rmt.Elems) { if (tok == token.DEFINE || tok == token.VAR) && len(lhs) > len(ac.rmt.Elems) {
a.diag("not enough values for definition"); a.diag("not enough values for definition");
bad = true;
} }
// Compile left type if there is one // Compile left type if there is one
var declType Type; var declType Type;
if declTypeExpr != nil { if declTypeExpr != nil {
declType = a.compileType(a.block, declTypeExpr); declType = a.compileType(a.block, declTypeExpr);
if declType == nil {
bad = true;
}
} }
// Compile left side // Compile left side
@ -486,7 +521,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
ident, ok = le.(*ast.Ident); ident, ok = le.(*ast.Ident);
if !ok { if !ok {
a.diagAt(le, "left side of := must be a name"); a.diagAt(le, "left side of := must be a name");
bad = true;
// Suppress new defitions errors // Suppress new defitions errors
nDefs++; nDefs++;
continue; continue;
@ -549,7 +583,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
// If it's a definition, define the identifier // If it's a definition, define the identifier
if ident != nil { if ident != nil {
if a.defineVar(ident, lt) == nil { if a.defineVar(ident, lt) == nil {
bad = true;
continue; continue;
} }
} }
@ -557,7 +590,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
// Compile LHS // Compile LHS
ls[i] = a.compileExpr(a.block, false, le); ls[i] = a.compileExpr(a.block, false, le);
if ls[i] == nil { if ls[i] == nil {
bad = true;
continue; continue;
} }
@ -584,7 +616,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
}; };
} else if ls[i].evalAddr == nil { } else if ls[i].evalAddr == nil {
ls[i].diag("cannot assign to %s", ls[i].desc); ls[i].diag("cannot assign to %s", ls[i].desc);
bad = true;
continue; continue;
} }
} }
@ -598,7 +629,9 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
return; return;
} }
if bad { // If there have been errors, our arrays are full of nil's so
// get out of here now.
if nerr != a.numError() {
return; return;
} }
@ -665,7 +698,6 @@ func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token,
} }
}); });
} }
a.err = false;
} }
var assignOpToOp = map[token.Token] token.Token { var assignOpToOp = map[token.Token] token.Token {
@ -721,10 +753,9 @@ func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) {
effect(v.f); effect(v.f);
assign(lf(v.f), v.f); assign(lf(v.f), v.f);
}); });
a.err = false;
} }
func (a *stmtCompiler) DoAssignStmt(s *ast.AssignStmt) { func (a *stmtCompiler) compileAssignStmt(s *ast.AssignStmt) {
switch s.Tok { switch s.Tok {
case token.ASSIGN, token.DEFINE: case token.ASSIGN, token.DEFINE:
a.doAssign(s.Lhs, s.Rhs, s.Tok, nil); a.doAssign(s.Lhs, s.Rhs, s.Tok, nil);
@ -734,15 +765,7 @@ func (a *stmtCompiler) DoAssignStmt(s *ast.AssignStmt) {
} }
} }
func (a *stmtCompiler) DoGoStmt(s *ast.GoStmt) { func (a *stmtCompiler) compileReturnStmt(s *ast.ReturnStmt) {
log.Crash("Not implemented");
}
func (a *stmtCompiler) DoDeferStmt(s *ast.DeferStmt) {
log.Crash("Not implemented");
}
func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
if a.fnType == nil { if a.fnType == nil {
a.diag("cannot return at the top level"); a.diag("cannot return at the top level");
return; return;
@ -752,7 +775,6 @@ func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
// Simple case. Simply exit from the function. // Simple case. Simply exit from the function.
a.flow.putTerm(); a.flow.putTerm();
a.push(func(v *vm) { v.pc = returnPC }); a.push(func(v *vm) { v.pc = returnPC });
a.err = false;
return; return;
} }
@ -779,9 +801,6 @@ func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
// returned from the called function will be returned from // returned from the called function will be returned from
// this one. // this one.
assign := a.compileAssign(s.Pos(), bc.block, NewMultiType(a.fnType.Out), rs, "return", "value"); assign := a.compileAssign(s.Pos(), bc.block, NewMultiType(a.fnType.Out), rs, "return", "value");
if assign == nil {
return;
}
// XXX(Spec) "The result types of the current function and the // XXX(Spec) "The result types of the current function and the
// called function must match." Match is fuzzy. It should // called function must match." Match is fuzzy. It should
@ -795,7 +814,6 @@ func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
assign(multiV(v.f.Vars[start:start+nout]), v.f); assign(multiV(v.f.Vars[start:start+nout]), v.f);
v.pc = returnPC; v.pc = returnPC;
}); });
a.err = false;
} }
func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, errOp, errCtx string) *label { func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, errOp, errCtx string) *label {
@ -824,7 +842,7 @@ func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool,
return nil; return nil;
} }
func (a *stmtCompiler) DoBranchStmt(s *ast.BranchStmt) { func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) {
var pc *uint; var pc *uint;
switch s.Tok { switch s.Tok {
@ -863,18 +881,15 @@ func (a *stmtCompiler) DoBranchStmt(s *ast.BranchStmt) {
a.flow.put1(false, pc); a.flow.put1(false, pc);
a.push(func(v *vm) { v.pc = *pc }); a.push(func(v *vm) { v.pc = *pc });
a.err = false;
} }
func (a *stmtCompiler) DoBlockStmt(s *ast.BlockStmt) { func (a *stmtCompiler) compileBlockStmt(s *ast.BlockStmt) {
bc := a.enterChild(); bc := a.enterChild();
bc.compileStmts(s); bc.compileStmts(s);
bc.exit(); bc.exit();
a.err = false;
} }
func (a *stmtCompiler) DoIfStmt(s *ast.IfStmt) { func (a *stmtCompiler) compileIfStmt(s *ast.IfStmt) {
// The scope of any variables declared by [the init] statement // The scope of any variables declared by [the init] statement
// extends to the end of the "if" statement and the variables // extends to the end of the "if" statement and the variables
// are initialized once before the statement is entered. // are initialized once before the statement is entered.
@ -898,15 +913,13 @@ func (a *stmtCompiler) DoIfStmt(s *ast.IfStmt) {
// Compile condition, if any. If there is no condition, we // Compile condition, if any. If there is no condition, we
// fall through to the body. // fall through to the body.
bad := false;
if s.Cond != nil { if s.Cond != nil {
e := bc.compileExpr(bc.block, false, s.Cond); e := bc.compileExpr(bc.block, false, s.Cond);
switch { switch {
case e == nil: case e == nil:
bad = true; // Error reported by compileExpr
case !e.t.isBoolean(): case !e.t.isBoolean():
e.diag("'if' condition must be boolean\n\t%v", e.t); e.diag("'if' condition must be boolean\n\t%v", e.t);
bad = true;
default: default:
eval := e.asBool(); eval := e.asBool();
a.flow.put1(true, &elsePC); a.flow.put1(true, &elsePC);
@ -936,17 +949,9 @@ func (a *stmtCompiler) DoIfStmt(s *ast.IfStmt) {
elsePC = a.nextPC(); elsePC = a.nextPC();
} }
endPC = a.nextPC(); endPC = a.nextPC();
if !bad {
a.err = false;
}
} }
func (a *stmtCompiler) DoCaseClause(s *ast.CaseClause) { func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
a.diag("case clause outside switch");
}
func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
// Create implicit scope around switch // Create implicit scope around switch
bc := a.enterChild(); bc := a.enterChild();
defer bc.exit(); defer bc.exit();
@ -959,17 +964,11 @@ func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
// Compile condition, if any, and extract its effects // Compile condition, if any, and extract its effects
var cond *expr; var cond *expr;
condbc := bc.enterChild(); condbc := bc.enterChild();
bad := false;
if s.Tag != nil { if s.Tag != nil {
e := condbc.compileExpr(condbc.block, false, s.Tag); e := condbc.compileExpr(condbc.block, false, s.Tag);
if e == nil { if e != nil {
bad = true;
} else {
var effect func(f *Frame); var effect func(f *Frame);
effect, cond = e.extractEffect(condbc.block, "switch"); effect, cond = e.extractEffect(condbc.block, "switch");
if effect == nil {
bad = true;
}
a.push(func(v *vm) { effect(v.f) }); a.push(func(v *vm) { effect(v.f) });
} }
} }
@ -981,13 +980,11 @@ func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
clause, ok := c.(*ast.CaseClause); clause, ok := c.(*ast.CaseClause);
if !ok { if !ok {
a.diagAt(clause, "switch statement must contain case clauses"); a.diagAt(clause, "switch statement must contain case clauses");
bad = true;
continue; continue;
} }
if clause.Values == nil { if clause.Values == nil {
if hasDefault { if hasDefault {
a.diagAt(clause, "switch statement contains more than one default case"); a.diagAt(clause, "switch statement contains more than one default case");
bad = true;
} }
hasDefault = true; hasDefault = true;
} else { } else {
@ -1007,19 +1004,16 @@ func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
e := condbc.compileExpr(condbc.block, false, v); e := condbc.compileExpr(condbc.block, false, v);
switch { switch {
case e == nil: case e == nil:
bad = true; // Error reported by compileExpr
case cond == nil && !e.t.isBoolean(): case cond == nil && !e.t.isBoolean():
a.diagAt(v, "'case' condition must be boolean"); a.diagAt(v, "'case' condition must be boolean");
bad = true;
case cond == nil: case cond == nil:
cases[i] = e.asBool(); cases[i] = e.asBool();
case cond != nil: case cond != nil:
// Create comparison // Create comparison
// TOOD(austin) This produces bad error messages // TOOD(austin) This produces bad error messages
compare := e.compileBinaryExpr(token.EQL, cond, e); compare := e.compileBinaryExpr(token.EQL, cond, e);
if compare == nil { if compare != nil {
bad = true;
} else {
cases[i] = compare.asBool(); cases[i] = compare.asBool();
} }
} }
@ -1031,18 +1025,16 @@ func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
casePCs := make([]*uint, ncases+1); casePCs := make([]*uint, ncases+1);
endPC := badPC; endPC := badPC;
if !bad { a.flow.put(false, false, casePCs);
a.flow.put(false, false, casePCs); a.push(func(v *vm) {
a.push(func(v *vm) { for i, c := range cases {
for i, c := range cases { if c(v.f) {
if c(v.f) { v.pc = *casePCs[i];
v.pc = *casePCs[i]; return;
return;
}
} }
v.pc = *casePCs[ncases]; }
}); v.pc = *casePCs[ncases];
} });
condbc.exit(); condbc.exit();
// Compile cases // Compile cases
@ -1080,7 +1072,6 @@ func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
// statements. // statements.
if _, ok := s2.(*ast.EmptyStmt); !ok { if _, ok := s2.(*ast.EmptyStmt); !ok {
a.diagAt(s, "fallthrough statement must be final statement in case"); a.diagAt(s, "fallthrough statement must be final statement in case");
bad = true;
break; break;
} }
} }
@ -1101,29 +1092,9 @@ func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
if !hasDefault { if !hasDefault {
casePCs[ncases] = &endPC; casePCs[ncases] = &endPC;
} }
if !bad {
a.err = false;
}
} }
func (a *stmtCompiler) DoTypeCaseClause(s *ast.TypeCaseClause) { func (a *stmtCompiler) compileForStmt(s *ast.ForStmt) {
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) {
// Wrap the entire for in a block. // Wrap the entire for in a block.
bc := a.enterChild(); bc := a.enterChild();
defer bc.exit(); defer bc.exit();
@ -1166,7 +1137,6 @@ func (a *stmtCompiler) DoForStmt(s *ast.ForStmt) {
} }
// Compile condition check, if any // Compile condition check, if any
bad := false;
checkPC = a.nextPC(); checkPC = a.nextPC();
if s.Cond == nil { if s.Cond == nil {
// If the condition is absent, it is equivalent to true. // If the condition is absent, it is equivalent to true.
@ -1176,10 +1146,9 @@ func (a *stmtCompiler) DoForStmt(s *ast.ForStmt) {
e := bc.compileExpr(bc.block, false, s.Cond); e := bc.compileExpr(bc.block, false, s.Cond);
switch { switch {
case e == nil: case e == nil:
bad = true; // Error reported by compileExpr
case !e.t.isBoolean(): case !e.t.isBoolean():
a.diag("'for' condition must be boolean\n\t%v", e.t); a.diag("'for' condition must be boolean\n\t%v", e.t);
bad = true;
default: default:
eval := e.asBool(); eval := e.asBool();
a.flow.put1(true, &bodyPC); a.flow.put1(true, &bodyPC);
@ -1192,14 +1161,6 @@ func (a *stmtCompiler) DoForStmt(s *ast.ForStmt) {
} }
endPC = a.nextPC(); endPC = a.nextPC();
if !bad {
a.err = false;
}
}
func (a *stmtCompiler) DoRangeStmt(s *ast.RangeStmt) {
log.Crash("Not implemented");
} }
/* /*
@ -1207,15 +1168,8 @@ func (a *stmtCompiler) DoRangeStmt(s *ast.RangeStmt) {
*/ */
func (a *blockCompiler) compileStmt(s ast.Stmt) { func (a *blockCompiler) compileStmt(s ast.Stmt) {
if a.block.inner != nil { sc := &stmtCompiler{a, s.Pos(), nil};
log.Crash("Child scope still entered"); sc.compile(s);
}
sc := &stmtCompiler{a, s.Pos(), nil, true};
s.Visit(sc);
if a.block.inner != nil {
log.Crash("Forgot to exit child scope");
}
a.err = a.err || sc.err;
} }
func (a *blockCompiler) compileStmts(block *ast.BlockStmt) { func (a *blockCompiler) compileStmts(block *ast.BlockStmt) {
@ -1272,7 +1226,6 @@ func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) (f
codeBuf: cb, codeBuf: cb,
flow: newFlowBuf(cb), flow: newFlowBuf(cb),
labels: make(map[string] *label), labels: make(map[string] *label),
err: false,
}; };
bc := &blockCompiler{ bc := &blockCompiler{
funcCompiler: fc, funcCompiler: fc,
@ -1280,10 +1233,10 @@ func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) (f
}; };
// Compile body // Compile body
nerr := a.numError();
bc.compileStmts(body); bc.compileStmts(body);
fc.checkLabels(); fc.checkLabels();
if nerr != a.numError() {
if fc.err {
return nil; return nil;
} }
@ -1303,15 +1256,13 @@ func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) (f
// Checks that labels were resolved and that all jumps obey scoping // Checks that labels were resolved and that all jumps obey scoping
// rules. Reports an error and set fc.err if any check fails. // rules. Reports an error and set fc.err if any check fails.
func (a *funcCompiler) checkLabels() { func (a *funcCompiler) checkLabels() {
bad := false; nerr := a.numError();
for _, l := range a.labels { for _, l := range a.labels {
if !l.resolved.IsValid() { if !l.resolved.IsValid() {
a.diagAt(&l.used, "label %s not defined", l.name); a.diagAt(&l.used, "label %s not defined", l.name);
bad = true;
} }
} }
if bad { if nerr != a.numError() {
a.err = true;
// Don't check scopes if we have unresolved labels // Don't check scopes if we have unresolved labels
return; return;
} }
@ -1319,9 +1270,7 @@ func (a *funcCompiler) checkLabels() {
// Executing the "goto" statement must not cause any variables // Executing the "goto" statement must not cause any variables
// to come into scope that were not already in scope at the // to come into scope that were not already in scope at the
// point of the goto. // point of the goto.
if !a.flow.gotosObeyScopes(a.compiler) { a.flow.gotosObeyScopes(a.compiler);
a.err = true;
}
} }
/* /*
@ -1338,7 +1287,7 @@ func (s *Stmt) Exec(f *Frame) {
func CompileStmts(scope *Scope, stmts []ast.Stmt) (*Stmt, os.Error) { func CompileStmts(scope *Scope, stmts []ast.Stmt) (*Stmt, os.Error) {
errors := scanner.NewErrorVector(); errors := scanner.NewErrorVector();
cc := &compiler{errors}; cc := &compiler{errors, 0, 0};
cb := newCodeBuf(); cb := newCodeBuf();
fc := &funcCompiler{ fc := &funcCompiler{
compiler: cc, compiler: cc,
@ -1347,18 +1296,18 @@ func CompileStmts(scope *Scope, stmts []ast.Stmt) (*Stmt, os.Error) {
codeBuf: cb, codeBuf: cb,
flow: newFlowBuf(cb), flow: newFlowBuf(cb),
labels: make(map[string] *label), labels: make(map[string] *label),
err: false,
}; };
bc := &blockCompiler{ bc := &blockCompiler{
funcCompiler: fc, funcCompiler: fc,
block: scope.block, block: scope.block,
}; };
out := make([]*Stmt, len(stmts)); out := make([]*Stmt, len(stmts));
nerr := cc.numError();
for i, stmt := range stmts { for i, stmt := range stmts {
bc.compileStmt(stmt); bc.compileStmt(stmt);
} }
fc.checkLabels(); fc.checkLabels();
if fc.err { if nerr != cc.numError() {
return nil, errors.GetError(scanner.Sorted); return nil, errors.GetError(scanner.Sorted);
} }
code := fc.get(); code := fc.get();