woodpecker/vendor/github.com/quasilyte/go-ruleguard/internal/gogrep/compile.go
Lukas c28f7cb29f
Add golangci-lint (#502)
Initial part of #435
2021-11-14 21:01:54 +01:00

976 lines
22 KiB
Go

package gogrep
import (
"fmt"
"go/ast"
"go/token"
)
type compileError string
func (e compileError) Error() string { return string(e) }
type compiler struct {
prog *program
stringIndexes map[string]uint8
ifaceIndexes map[interface{}]uint8
strict bool
fset *token.FileSet
}
func (c *compiler) Compile(fset *token.FileSet, root ast.Node, strict bool) (p *program, err error) {
defer func() {
if err != nil {
return
}
rv := recover()
if rv == nil {
return
}
if parseErr, ok := rv.(compileError); ok {
err = parseErr
return
}
panic(rv) // Not our panic
}()
c.fset = fset
c.strict = strict
c.prog = &program{
insts: make([]instruction, 0, 8),
}
c.stringIndexes = make(map[string]uint8)
c.ifaceIndexes = make(map[interface{}]uint8)
c.compileNode(root)
if len(c.prog.insts) == 0 {
return nil, c.errorf(root, "0 instructions generated")
}
return c.prog, nil
}
func (c *compiler) errorf(n ast.Node, format string, args ...interface{}) compileError {
loc := c.fset.Position(n.Pos())
message := fmt.Sprintf("%s:%d: %s", loc.Filename, loc.Line, fmt.Sprintf(format, args...))
return compileError(message)
}
func (c *compiler) toUint8(n ast.Node, v int) uint8 {
if !fitsUint8(v) {
panic(c.errorf(n, "implementation error: %v can't be converted to uint8", v))
}
return uint8(v)
}
func (c *compiler) internString(n ast.Node, s string) uint8 {
if index, ok := c.stringIndexes[s]; ok {
return index
}
index := len(c.prog.strings)
if !fitsUint8(index) {
panic(c.errorf(n, "implementation limitation: too many string values"))
}
c.stringIndexes[s] = uint8(index)
c.prog.strings = append(c.prog.strings, s)
return uint8(index)
}
func (c *compiler) internIface(n ast.Node, v interface{}) uint8 {
if index, ok := c.ifaceIndexes[v]; ok {
return index
}
index := len(c.prog.ifaces)
if !fitsUint8(index) {
panic(c.errorf(n, "implementation limitation: too many values"))
}
c.ifaceIndexes[v] = uint8(index)
c.prog.ifaces = append(c.prog.ifaces, v)
return uint8(index)
}
func (c *compiler) emitInst(inst instruction) {
c.prog.insts = append(c.prog.insts, inst)
}
func (c *compiler) emitInstOp(op operation) {
c.emitInst(instruction{op: op})
}
func (c *compiler) compileNode(n ast.Node) {
switch n := n.(type) {
case *ast.File:
c.compileFile(n)
case ast.Decl:
c.compileDecl(n)
case ast.Expr:
c.compileExpr(n)
case ast.Stmt:
c.compileStmt(n)
case *ast.ValueSpec:
c.compileValueSpec(n)
case stmtSlice:
c.compileStmtSlice(n)
case exprSlice:
c.compileExprSlice(n)
default:
panic(c.errorf(n, "compileNode: unexpected %T", n))
}
}
func (c *compiler) compileOptStmt(n ast.Stmt) {
if exprStmt, ok := n.(*ast.ExprStmt); ok {
if ident, ok := exprStmt.X.(*ast.Ident); ok && isWildName(ident.Name) {
c.compileWildIdent(ident, true)
return
}
}
c.compileStmt(n)
}
func (c *compiler) compileOptExpr(n ast.Expr) {
if ident, ok := n.(*ast.Ident); ok && isWildName(ident.Name) {
c.compileWildIdent(ident, true)
return
}
c.compileExpr(n)
}
func (c *compiler) compileFieldList(n *ast.FieldList) {
c.emitInstOp(opFieldList)
for _, x := range n.List {
c.compileField(x)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileField(n *ast.Field) {
switch {
case len(n.Names) == 0:
c.emitInstOp(opUnnamedField)
case len(n.Names) == 1:
name := n.Names[0]
if isWildName(name.Name) {
c.emitInstOp(opField)
c.compileWildIdent(name, false)
} else {
c.emitInst(instruction{
op: opSimpleField,
valueIndex: c.internString(name, name.Name),
})
}
default:
c.emitInstOp(opMultiField)
for _, name := range n.Names {
c.compileIdent(name)
}
c.emitInstOp(opEnd)
}
c.compileExpr(n.Type)
}
func (c *compiler) compileValueSpec(spec *ast.ValueSpec) {
switch {
case spec.Type == nil:
c.emitInstOp(opValueInitSpec)
case len(spec.Values) == 0:
c.emitInstOp(opTypedValueSpec)
default:
c.emitInstOp(opTypedValueInitSpec)
}
for _, name := range spec.Names {
c.compileIdent(name)
}
c.emitInstOp(opEnd)
if spec.Type != nil {
c.compileExpr(spec.Type)
}
if len(spec.Values) != 0 {
for _, v := range spec.Values {
c.compileExpr(v)
}
c.emitInstOp(opEnd)
}
}
func (c *compiler) compileTypeSpec(spec *ast.TypeSpec) {
c.emitInstOp(pickOp(spec.Assign.IsValid(), opTypeAliasSpec, opTypeSpec))
c.compileIdent(spec.Name)
c.compileExpr(spec.Type)
}
func (c *compiler) compileFile(n *ast.File) {
if len(n.Imports) == 0 && len(n.Decls) == 0 {
c.emitInstOp(opEmptyPackage)
c.compileIdent(n.Name)
return
}
panic(c.errorf(n, "compileFile: unsupported file pattern"))
}
func (c *compiler) compileDecl(n ast.Decl) {
switch n := n.(type) {
case *ast.FuncDecl:
c.compileFuncDecl(n)
case *ast.GenDecl:
c.compileGenDecl(n)
default:
panic(c.errorf(n, "compileDecl: unexpected %T", n))
}
}
func (c *compiler) compileFuncDecl(n *ast.FuncDecl) {
if n.Recv == nil {
c.emitInstOp(pickOp(n.Body == nil, opFuncProtoDecl, opFuncDecl))
} else {
c.emitInstOp(pickOp(n.Body == nil, opMethodProtoDecl, opMethodDecl))
}
if n.Recv != nil {
c.compileFieldList(n.Recv)
}
c.compileIdent(n.Name)
c.compileFuncType(n.Type)
if n.Body != nil {
c.compileBlockStmt(n.Body)
}
}
func (c *compiler) compileGenDecl(n *ast.GenDecl) {
switch n.Tok {
case token.CONST, token.VAR:
c.emitInstOp(pickOp(n.Tok == token.CONST, opConstDecl, opVarDecl))
for _, spec := range n.Specs {
c.compileValueSpec(spec.(*ast.ValueSpec))
}
c.emitInstOp(opEnd)
case token.TYPE:
c.emitInstOp(opTypeDecl)
for _, spec := range n.Specs {
c.compileTypeSpec(spec.(*ast.TypeSpec))
}
c.emitInstOp(opEnd)
default:
panic(c.errorf(n, "unexpected gen decl"))
}
}
func (c *compiler) compileExpr(n ast.Expr) {
switch n := n.(type) {
case *ast.BasicLit:
c.compileBasicLit(n)
case *ast.BinaryExpr:
c.compileBinaryExpr(n)
case *ast.IndexExpr:
c.compileIndexExpr(n)
case *ast.Ident:
c.compileIdent(n)
case *ast.CallExpr:
c.compileCallExpr(n)
case *ast.UnaryExpr:
c.compileUnaryExpr(n)
case *ast.StarExpr:
c.compileStarExpr(n)
case *ast.ParenExpr:
c.compileParenExpr(n)
case *ast.SliceExpr:
c.compileSliceExpr(n)
case *ast.FuncType:
c.compileFuncType(n)
case *ast.ArrayType:
c.compileArrayType(n)
case *ast.MapType:
c.compileMapType(n)
case *ast.ChanType:
c.compileChanType(n)
case *ast.CompositeLit:
c.compileCompositeLit(n)
case *ast.FuncLit:
c.compileFuncLit(n)
case *ast.Ellipsis:
c.compileEllipsis(n)
case *ast.KeyValueExpr:
c.compileKeyValueExpr(n)
case *ast.SelectorExpr:
c.compileSelectorExpr(n)
case *ast.TypeAssertExpr:
c.compileTypeAssertExpr(n)
default:
panic(c.errorf(n, "compileExpr: unexpected %T", n))
}
}
func (c *compiler) compileBasicLit(n *ast.BasicLit) {
if !c.strict {
v := literalValue(n)
if v == nil {
panic(c.errorf(n, "can't convert %s (%s) value", n.Value, n.Kind))
}
c.prog.insts = append(c.prog.insts, instruction{
op: opBasicLit,
valueIndex: c.internIface(n, v),
})
return
}
var inst instruction
switch n.Kind {
case token.INT:
inst.op = opStrictIntLit
case token.FLOAT:
inst.op = opStrictFloatLit
case token.STRING:
inst.op = opStrictStringLit
case token.CHAR:
inst.op = opStrictCharLit
default:
inst.op = opStrictComplexLit
}
inst.valueIndex = c.internString(n, n.Value)
c.prog.insts = append(c.prog.insts, inst)
}
func (c *compiler) compileBinaryExpr(n *ast.BinaryExpr) {
c.prog.insts = append(c.prog.insts, instruction{
op: opBinaryExpr,
value: c.toUint8(n, int(n.Op)),
})
c.compileExpr(n.X)
c.compileExpr(n.Y)
}
func (c *compiler) compileIndexExpr(n *ast.IndexExpr) {
c.emitInstOp(opIndexExpr)
c.compileExpr(n.X)
c.compileExpr(n.Index)
}
func (c *compiler) compileWildIdent(n *ast.Ident, optional bool) {
info := decodeWildName(n.Name)
var inst instruction
switch {
case info.Name == "_" && !info.Seq:
inst.op = opNode
case info.Name == "_" && info.Seq:
inst.op = pickOp(optional, opOptNode, opNodeSeq)
case info.Name != "_" && !info.Seq:
inst.op = opNamedNode
inst.valueIndex = c.internString(n, info.Name)
default:
inst.op = pickOp(optional, opNamedOptNode, opNamedNodeSeq)
inst.valueIndex = c.internString(n, info.Name)
}
c.prog.insts = append(c.prog.insts, inst)
}
func (c *compiler) compileIdent(n *ast.Ident) {
if isWildName(n.Name) {
c.compileWildIdent(n, false)
return
}
c.prog.insts = append(c.prog.insts, instruction{
op: opIdent,
valueIndex: c.internString(n, n.Name),
})
}
func (c *compiler) compileCallExpr(n *ast.CallExpr) {
op := opCallExpr
if n.Ellipsis.IsValid() {
op = opVariadicCallExpr
}
c.emitInstOp(op)
c.compileExpr(n.Fun)
for _, arg := range n.Args {
c.compileExpr(arg)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileUnaryExpr(n *ast.UnaryExpr) {
c.prog.insts = append(c.prog.insts, instruction{
op: opUnaryExpr,
value: c.toUint8(n, int(n.Op)),
})
c.compileExpr(n.X)
}
func (c *compiler) compileStarExpr(n *ast.StarExpr) {
c.emitInstOp(opStarExpr)
c.compileExpr(n.X)
}
func (c *compiler) compileParenExpr(n *ast.ParenExpr) {
c.emitInstOp(opParenExpr)
c.compileExpr(n.X)
}
func (c *compiler) compileSliceExpr(n *ast.SliceExpr) {
switch {
case n.Low == nil && n.High == nil && !n.Slice3:
c.emitInstOp(opSliceExpr)
c.compileExpr(n.X)
case n.Low != nil && n.High == nil && !n.Slice3:
c.emitInstOp(opSliceFromExpr)
c.compileExpr(n.X)
c.compileExpr(n.Low)
case n.Low == nil && n.High != nil && !n.Slice3:
c.emitInstOp(opSliceToExpr)
c.compileExpr(n.X)
c.compileExpr(n.High)
case n.Low != nil && n.High != nil && !n.Slice3:
c.emitInstOp(opSliceFromToExpr)
c.compileExpr(n.X)
c.compileExpr(n.Low)
c.compileExpr(n.High)
case n.Low == nil && n.Slice3:
c.emitInstOp(opSliceToCapExpr)
c.compileExpr(n.X)
c.compileExpr(n.High)
c.compileExpr(n.Max)
case n.Low != nil && n.Slice3:
c.emitInstOp(opSliceFromToCapExpr)
c.compileExpr(n.X)
c.compileExpr(n.Low)
c.compileExpr(n.High)
c.compileExpr(n.Max)
default:
panic(c.errorf(n, "unexpected slice expr"))
}
}
func (c *compiler) compileFuncType(n *ast.FuncType) {
void := n.Results == nil || len(n.Results.List) == 0
if void {
c.emitInstOp(opVoidFuncType)
} else {
c.emitInstOp(opFuncType)
}
c.compileFieldList(n.Params)
if !void {
c.compileFieldList(n.Results)
}
}
func (c *compiler) compileArrayType(n *ast.ArrayType) {
if n.Len == nil {
c.emitInstOp(opSliceType)
c.compileExpr(n.Elt)
} else {
c.emitInstOp(opArrayType)
c.compileExpr(n.Len)
c.compileExpr(n.Elt)
}
}
func (c *compiler) compileMapType(n *ast.MapType) {
c.emitInstOp(opMapType)
c.compileExpr(n.Key)
c.compileExpr(n.Value)
}
func (c *compiler) compileChanType(n *ast.ChanType) {
c.emitInst(instruction{
op: opChanType,
value: c.toUint8(n, int(n.Dir)),
})
c.compileExpr(n.Value)
}
func (c *compiler) compileCompositeLit(n *ast.CompositeLit) {
if n.Type == nil {
c.emitInstOp(opCompositeLit)
} else {
c.emitInstOp(opTypedCompositeLit)
c.compileExpr(n.Type)
}
for _, elt := range n.Elts {
c.compileExpr(elt)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileFuncLit(n *ast.FuncLit) {
c.emitInstOp(opFuncLit)
c.compileFuncType(n.Type)
c.compileBlockStmt(n.Body)
}
func (c *compiler) compileEllipsis(n *ast.Ellipsis) {
if n.Elt == nil {
c.emitInstOp(opEllipsis)
} else {
c.emitInstOp(opTypedEllipsis)
c.compileExpr(n.Elt)
}
}
func (c *compiler) compileKeyValueExpr(n *ast.KeyValueExpr) {
c.emitInstOp(opKeyValueExpr)
c.compileExpr(n.Key)
c.compileExpr(n.Value)
}
func (c *compiler) compileSelectorExpr(n *ast.SelectorExpr) {
if isWildName(n.Sel.Name) {
c.emitInstOp(opSelectorExpr)
c.compileWildIdent(n.Sel, false)
c.compileExpr(n.X)
return
}
c.prog.insts = append(c.prog.insts, instruction{
op: opSimpleSelectorExpr,
valueIndex: c.internString(n.Sel, n.Sel.String()),
})
c.compileExpr(n.X)
}
func (c *compiler) compileTypeAssertExpr(n *ast.TypeAssertExpr) {
if n.Type != nil {
c.emitInstOp(opTypeAssertExpr)
c.compileExpr(n.X)
c.compileExpr(n.Type)
} else {
c.emitInstOp(opTypeSwitchAssertExpr)
c.compileExpr(n.X)
}
}
func (c *compiler) compileStmt(n ast.Stmt) {
switch n := n.(type) {
case *ast.AssignStmt:
c.compileAssignStmt(n)
case *ast.BlockStmt:
c.compileBlockStmt(n)
case *ast.ExprStmt:
c.compileExprStmt(n)
case *ast.IfStmt:
c.compileIfStmt(n)
case *ast.CaseClause:
c.compileCaseClause(n)
case *ast.SwitchStmt:
c.compileSwitchStmt(n)
case *ast.TypeSwitchStmt:
c.compileTypeSwitchStmt(n)
case *ast.SelectStmt:
c.compileSelectStmt(n)
case *ast.ForStmt:
c.compileForStmt(n)
case *ast.RangeStmt:
c.compileRangeStmt(n)
case *ast.IncDecStmt:
c.compileIncDecStmt(n)
case *ast.EmptyStmt:
c.compileEmptyStmt(n)
case *ast.ReturnStmt:
c.compileReturnStmt(n)
case *ast.BranchStmt:
c.compileBranchStmt(n)
case *ast.LabeledStmt:
c.compileLabeledStmt(n)
case *ast.GoStmt:
c.compileGoStmt(n)
case *ast.DeferStmt:
c.compileDeferStmt(n)
case *ast.SendStmt:
c.compileSendStmt(n)
case *ast.DeclStmt:
c.compileDecl(n.Decl)
default:
panic(c.errorf(n, "compileStmt: unexpected %T", n))
}
}
func (c *compiler) compileAssignStmt(n *ast.AssignStmt) {
if len(n.Lhs) == 1 && len(n.Rhs) == 1 {
lhsInfo := decodeWildNode(n.Lhs[0])
rhsInfo := decodeWildNode(n.Rhs[0])
if !lhsInfo.Seq && !rhsInfo.Seq {
c.emitInst(instruction{
op: opAssignStmt,
value: uint8(n.Tok),
})
c.compileExpr(n.Lhs[0])
c.compileExpr(n.Rhs[0])
return
}
}
c.emitInst(instruction{
op: opMultiAssignStmt,
value: uint8(n.Tok),
})
for _, x := range n.Lhs {
c.compileExpr(x)
}
c.emitInstOp(opEnd)
for _, x := range n.Rhs {
c.compileExpr(x)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileBlockStmt(n *ast.BlockStmt) {
c.emitInstOp(opBlockStmt)
for _, elt := range n.List {
c.compileStmt(elt)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileExprStmt(n *ast.ExprStmt) {
if ident, ok := n.X.(*ast.Ident); ok && isWildName(ident.Name) {
c.compileIdent(ident)
} else {
c.emitInstOp(opExprStmt)
c.compileExpr(n.X)
}
}
func (c *compiler) compileIfStmt(n *ast.IfStmt) {
// Check for the special case: `if $*_ ...` should match all if statements.
if ident, ok := n.Cond.(*ast.Ident); ok && n.Init == nil && isWildName(ident.Name) {
info := decodeWildName(ident.Name)
if info.Seq && info.Name == "_" {
// Set Init to Cond, change cond from $*_ to $_.
n.Init = &ast.ExprStmt{X: n.Cond}
cond := &ast.Ident{Name: encodeWildName(info.Name, false)}
n.Cond = cond
c.compileIfStmt(n)
return
}
// Named $* is harder and slower.
c.prog.insts = append(c.prog.insts, instruction{
op: pickOp(n.Else == nil, opIfNamedOptStmt, opIfNamedOptElseStmt),
valueIndex: c.internString(ident, info.Name),
})
c.compileStmt(n.Body)
if n.Else != nil {
c.compileStmt(n.Else)
}
return
}
switch {
case n.Init == nil && n.Else == nil:
c.emitInstOp(opIfStmt)
c.compileExpr(n.Cond)
c.compileStmt(n.Body)
case n.Init != nil && n.Else == nil:
c.emitInstOp(opIfInitStmt)
c.compileOptStmt(n.Init)
c.compileExpr(n.Cond)
c.compileStmt(n.Body)
case n.Init == nil && n.Else != nil:
c.emitInstOp(opIfElseStmt)
c.compileExpr(n.Cond)
c.compileStmt(n.Body)
c.compileStmt(n.Else)
case n.Init != nil && n.Else != nil:
c.emitInstOp(opIfInitElseStmt)
c.compileOptStmt(n.Init)
c.compileExpr(n.Cond)
c.compileStmt(n.Body)
c.compileStmt(n.Else)
default:
panic(c.errorf(n, "unexpected if stmt"))
}
}
func (c *compiler) compileCommClause(n *ast.CommClause) {
c.emitInstOp(pickOp(n.Comm == nil, opDefaultCommClause, opCommClause))
if n.Comm != nil {
c.compileStmt(n.Comm)
}
for _, x := range n.Body {
c.compileStmt(x)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileCaseClause(n *ast.CaseClause) {
c.emitInstOp(pickOp(n.List == nil, opDefaultCaseClause, opCaseClause))
if n.List != nil {
for _, x := range n.List {
c.compileExpr(x)
}
c.emitInstOp(opEnd)
}
for _, x := range n.Body {
c.compileStmt(x)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileSwitchBody(n *ast.BlockStmt) {
wildcardCase := func(cc *ast.CaseClause) *ast.Ident {
if len(cc.List) != 1 || len(cc.Body) != 1 {
return nil
}
v, ok := cc.List[0].(*ast.Ident)
if !ok || !isWildName(v.Name) {
return nil
}
bodyStmt, ok := cc.Body[0].(*ast.ExprStmt)
if !ok {
return nil
}
bodyIdent, ok := bodyStmt.X.(*ast.Ident)
if !ok || bodyIdent.Name != "gogrep_body" {
return nil
}
return v
}
for _, cc := range n.List {
cc := cc.(*ast.CaseClause)
wildcard := wildcardCase(cc)
if wildcard == nil {
c.compileCaseClause(cc)
continue
}
c.compileWildIdent(wildcard, false)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileSwitchStmt(n *ast.SwitchStmt) {
var op operation
switch {
case n.Init == nil && n.Tag == nil:
op = opSwitchStmt
case n.Init == nil && n.Tag != nil:
op = opSwitchTagStmt
case n.Init != nil && n.Tag == nil:
op = opSwitchInitStmt
default:
op = opSwitchInitTagStmt
}
c.emitInstOp(op)
if n.Init != nil {
c.compileOptStmt(n.Init)
}
if n.Tag != nil {
c.compileOptExpr(n.Tag)
}
c.compileSwitchBody(n.Body)
}
func (c *compiler) compileTypeSwitchStmt(n *ast.TypeSwitchStmt) {
c.emitInstOp(pickOp(n.Init == nil, opTypeSwitchStmt, opTypeSwitchInitStmt))
if n.Init != nil {
c.compileOptStmt(n.Init)
}
c.compileStmt(n.Assign)
c.compileSwitchBody(n.Body)
}
func (c *compiler) compileSelectStmt(n *ast.SelectStmt) {
c.emitInstOp(opSelectStmt)
wildcardCase := func(cc *ast.CommClause) *ast.Ident {
if cc.Comm == nil {
return nil
}
vStmt, ok := cc.Comm.(*ast.ExprStmt)
if !ok {
return nil
}
v, ok := vStmt.X.(*ast.Ident)
if !ok || !isWildName(v.Name) {
return nil
}
bodyStmt, ok := cc.Body[0].(*ast.ExprStmt)
if !ok {
return nil
}
bodyIdent, ok := bodyStmt.X.(*ast.Ident)
if !ok || bodyIdent.Name != "gogrep_body" {
return nil
}
return v
}
for _, cc := range n.Body.List {
cc := cc.(*ast.CommClause)
wildcard := wildcardCase(cc)
if wildcard == nil {
c.compileCommClause(cc)
continue
}
c.compileWildIdent(wildcard, false)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileForStmt(n *ast.ForStmt) {
var op operation
switch {
case n.Init == nil && n.Cond == nil && n.Post == nil:
op = opForStmt
case n.Init == nil && n.Cond == nil && n.Post != nil:
op = opForPostStmt
case n.Init == nil && n.Cond != nil && n.Post == nil:
op = opForCondStmt
case n.Init == nil && n.Cond != nil && n.Post != nil:
op = opForCondPostStmt
case n.Init != nil && n.Cond == nil && n.Post == nil:
op = opForInitStmt
case n.Init != nil && n.Cond == nil && n.Post != nil:
op = opForInitPostStmt
case n.Init != nil && n.Cond != nil && n.Post == nil:
op = opForInitCondStmt
default:
op = opForInitCondPostStmt
}
c.emitInstOp(op)
if n.Init != nil {
c.compileOptStmt(n.Init)
}
if n.Cond != nil {
c.compileOptExpr(n.Cond)
}
if n.Post != nil {
c.compileOptStmt(n.Post)
}
c.compileBlockStmt(n.Body)
}
func (c *compiler) compileRangeStmt(n *ast.RangeStmt) {
switch {
case n.Key == nil && n.Value == nil:
c.emitInstOp(opRangeStmt)
c.compileExpr(n.X)
c.compileStmt(n.Body)
case n.Key != nil && n.Value == nil:
c.emitInst(instruction{
op: opRangeKeyStmt,
value: c.toUint8(n, int(n.Tok)),
})
c.compileExpr(n.Key)
c.compileExpr(n.X)
c.compileStmt(n.Body)
case n.Key != nil && n.Value != nil:
c.emitInst(instruction{
op: opRangeKeyValueStmt,
value: c.toUint8(n, int(n.Tok)),
})
c.compileExpr(n.Key)
c.compileExpr(n.Value)
c.compileExpr(n.X)
c.compileStmt(n.Body)
default:
panic(c.errorf(n, "unexpected range stmt"))
}
}
func (c *compiler) compileIncDecStmt(n *ast.IncDecStmt) {
c.prog.insts = append(c.prog.insts, instruction{
op: opIncDecStmt,
value: c.toUint8(n, int(n.Tok)),
})
c.compileExpr(n.X)
}
func (c *compiler) compileEmptyStmt(n *ast.EmptyStmt) {
_ = n // unused
c.emitInstOp(opEmptyStmt)
}
func (c *compiler) compileReturnStmt(n *ast.ReturnStmt) {
c.emitInstOp(opReturnStmt)
for _, x := range n.Results {
c.compileExpr(x)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileBranchStmt(n *ast.BranchStmt) {
if n.Label != nil {
if isWildName(n.Label.Name) {
c.prog.insts = append(c.prog.insts, instruction{
op: opLabeledBranchStmt,
value: c.toUint8(n, int(n.Tok)),
})
c.compileWildIdent(n.Label, false)
} else {
c.prog.insts = append(c.prog.insts, instruction{
op: opSimpleLabeledBranchStmt,
value: c.toUint8(n, int(n.Tok)),
valueIndex: c.internString(n.Label, n.Label.Name),
})
}
return
}
c.prog.insts = append(c.prog.insts, instruction{
op: opBranchStmt,
value: c.toUint8(n, int(n.Tok)),
})
}
func (c *compiler) compileLabeledStmt(n *ast.LabeledStmt) {
if isWildName(n.Label.Name) {
c.emitInstOp(opLabeledStmt)
c.compileWildIdent(n.Label, false)
c.compileStmt(n.Stmt)
return
}
c.prog.insts = append(c.prog.insts, instruction{
op: opSimpleLabeledStmt,
valueIndex: c.internString(n.Label, n.Label.Name),
})
c.compileStmt(n.Stmt)
}
func (c *compiler) compileGoStmt(n *ast.GoStmt) {
c.emitInstOp(opGoStmt)
c.compileExpr(n.Call)
}
func (c *compiler) compileDeferStmt(n *ast.DeferStmt) {
c.emitInstOp(opDeferStmt)
c.compileExpr(n.Call)
}
func (c *compiler) compileSendStmt(n *ast.SendStmt) {
c.emitInstOp(opSendStmt)
c.compileExpr(n.Chan)
c.compileExpr(n.Value)
}
func (c *compiler) compileStmtSlice(stmts stmtSlice) {
c.emitInstOp(opMultiStmt)
for _, n := range stmts {
c.compileStmt(n)
}
c.emitInstOp(opEnd)
}
func (c *compiler) compileExprSlice(exprs exprSlice) {
c.emitInstOp(opMultiExpr)
for _, n := range exprs {
c.compileExpr(n)
}
c.emitInstOp(opEnd)
}
func pickOp(cond bool, ifTrue, ifFalse operation) operation {
if cond {
return ifTrue
}
return ifFalse
}
func fitsUint8(v int) bool {
return v >= 0 && v <= 0xff
}