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 }