in internal/compile/compile.go [1047:1240]
func (fcomp *fcomp) stmt(stmt syntax.Stmt) {
switch stmt := stmt.(type) {
case *syntax.ExprStmt:
if _, ok := stmt.X.(*syntax.Literal); ok {
// Opt: don't compile doc comments only to pop them.
return
}
fcomp.expr(stmt.X)
fcomp.emit(POP)
case *syntax.BranchStmt:
// Resolver invariant: break/continue appear only within loops.
switch stmt.Token {
case syntax.PASS:
// no-op
case syntax.BREAK:
b := fcomp.loops[len(fcomp.loops)-1].break_
fcomp.jump(b)
fcomp.block = fcomp.newBlock() // dead code
case syntax.CONTINUE:
b := fcomp.loops[len(fcomp.loops)-1].continue_
fcomp.jump(b)
fcomp.block = fcomp.newBlock() // dead code
}
case *syntax.IfStmt:
// Keep consistent with CondExpr.
t := fcomp.newBlock()
f := fcomp.newBlock()
done := fcomp.newBlock()
fcomp.ifelse(stmt.Cond, t, f)
fcomp.block = t
fcomp.stmts(stmt.True)
fcomp.jump(done)
fcomp.block = f
fcomp.stmts(stmt.False)
fcomp.jump(done)
fcomp.block = done
case *syntax.AssignStmt:
switch stmt.Op {
case syntax.EQ:
// simple assignment: x = y
fcomp.expr(stmt.RHS)
fcomp.assign(stmt.OpPos, stmt.LHS)
case syntax.PLUS_EQ,
syntax.MINUS_EQ,
syntax.STAR_EQ,
syntax.SLASH_EQ,
syntax.SLASHSLASH_EQ,
syntax.PERCENT_EQ,
syntax.AMP_EQ,
syntax.PIPE_EQ,
syntax.CIRCUMFLEX_EQ,
syntax.LTLT_EQ,
syntax.GTGT_EQ:
// augmented assignment: x += y
var set func()
// Evaluate "address" of x exactly once to avoid duplicate side-effects.
switch lhs := unparen(stmt.LHS).(type) {
case *syntax.Ident:
// x = ...
fcomp.lookup(lhs)
set = func() {
fcomp.set(lhs)
}
case *syntax.IndexExpr:
// x[y] = ...
fcomp.expr(lhs.X)
fcomp.expr(lhs.Y)
fcomp.emit(DUP2)
fcomp.setPos(lhs.Lbrack)
fcomp.emit(INDEX)
set = func() {
fcomp.setPos(lhs.Lbrack)
fcomp.emit(SETINDEX)
}
case *syntax.DotExpr:
// x.f = ...
fcomp.expr(lhs.X)
fcomp.emit(DUP)
name := fcomp.pcomp.nameIndex(lhs.Name.Name)
fcomp.setPos(lhs.Dot)
fcomp.emit1(ATTR, name)
set = func() {
fcomp.setPos(lhs.Dot)
fcomp.emit1(SETFIELD, name)
}
default:
panic(lhs)
}
fcomp.expr(stmt.RHS)
// In-place x+=y and x|=y have special semantics:
// the resulting x aliases the original x.
switch stmt.Op {
case syntax.PLUS_EQ:
fcomp.setPos(stmt.OpPos)
fcomp.emit(INPLACE_ADD)
case syntax.PIPE_EQ:
fcomp.setPos(stmt.OpPos)
fcomp.emit(INPLACE_PIPE)
default:
fcomp.binop(stmt.OpPos, stmt.Op-syntax.PLUS_EQ+syntax.PLUS)
}
set()
}
case *syntax.DefStmt:
fcomp.function(stmt.Function.(*resolve.Function))
fcomp.set(stmt.Name)
case *syntax.ForStmt:
// Keep consistent with ForClause.
head := fcomp.newBlock()
body := fcomp.newBlock()
tail := fcomp.newBlock()
fcomp.expr(stmt.X)
fcomp.setPos(stmt.For)
fcomp.emit(ITERPUSH)
fcomp.jump(head)
fcomp.block = head
fcomp.condjump(ITERJMP, tail, body)
fcomp.block = body
fcomp.assign(stmt.For, stmt.Vars)
fcomp.loops = append(fcomp.loops, loop{break_: tail, continue_: head})
fcomp.stmts(stmt.Body)
fcomp.loops = fcomp.loops[:len(fcomp.loops)-1]
fcomp.jump(head)
fcomp.block = tail
fcomp.emit(ITERPOP)
case *syntax.WhileStmt:
head := fcomp.newBlock()
body := fcomp.newBlock()
done := fcomp.newBlock()
fcomp.jump(head)
fcomp.block = head
fcomp.ifelse(stmt.Cond, body, done)
fcomp.block = body
fcomp.loops = append(fcomp.loops, loop{break_: done, continue_: head})
fcomp.stmts(stmt.Body)
fcomp.loops = fcomp.loops[:len(fcomp.loops)-1]
fcomp.jump(head)
fcomp.block = done
case *syntax.ReturnStmt:
if stmt.Result != nil {
fcomp.expr(stmt.Result)
} else {
fcomp.emit(NONE)
}
fcomp.emit(RETURN)
fcomp.block = fcomp.newBlock() // dead code
case *syntax.LoadStmt:
for i := range stmt.From {
fcomp.string(stmt.From[i].Name)
}
module := stmt.Module.Value.(string)
fcomp.pcomp.prog.Loads = append(fcomp.pcomp.prog.Loads, Binding{
Name: module,
Pos: stmt.Module.TokenPos,
})
fcomp.string(module)
fcomp.setPos(stmt.Load)
fcomp.emit1(LOAD, uint32(len(stmt.From)))
for i := range stmt.To {
fcomp.set(stmt.To[len(stmt.To)-1-i])
}
default:
start, _ := stmt.Span()
log.Panicf("%s: exec: unexpected statement %T", start, stmt)
}
}