1package gogrep
2
3import (
4	"fmt"
5	"go/ast"
6	"go/token"
7)
8
9type compileError string
10
11func (e compileError) Error() string { return string(e) }
12
13type compiler struct {
14	prog          *program
15	stringIndexes map[string]uint8
16	ifaceIndexes  map[interface{}]uint8
17	strict        bool
18	fset          *token.FileSet
19}
20
21func (c *compiler) Compile(fset *token.FileSet, root ast.Node, strict bool) (p *program, err error) {
22	defer func() {
23		if err != nil {
24			return
25		}
26		rv := recover()
27		if rv == nil {
28			return
29		}
30		if parseErr, ok := rv.(compileError); ok {
31			err = parseErr
32			return
33		}
34		panic(rv) // Not our panic
35	}()
36
37	c.fset = fset
38	c.strict = strict
39	c.prog = &program{
40		insts: make([]instruction, 0, 8),
41	}
42	c.stringIndexes = make(map[string]uint8)
43	c.ifaceIndexes = make(map[interface{}]uint8)
44
45	c.compileNode(root)
46
47	if len(c.prog.insts) == 0 {
48		return nil, c.errorf(root, "0 instructions generated")
49	}
50
51	return c.prog, nil
52}
53
54func (c *compiler) errorf(n ast.Node, format string, args ...interface{}) compileError {
55	loc := c.fset.Position(n.Pos())
56	message := fmt.Sprintf("%s:%d: %s", loc.Filename, loc.Line, fmt.Sprintf(format, args...))
57	return compileError(message)
58}
59
60func (c *compiler) toUint8(n ast.Node, v int) uint8 {
61	if !fitsUint8(v) {
62		panic(c.errorf(n, "implementation error: %v can't be converted to uint8", v))
63	}
64	return uint8(v)
65}
66
67func (c *compiler) internString(n ast.Node, s string) uint8 {
68	if index, ok := c.stringIndexes[s]; ok {
69		return index
70	}
71	index := len(c.prog.strings)
72	if !fitsUint8(index) {
73		panic(c.errorf(n, "implementation limitation: too many string values"))
74	}
75	c.stringIndexes[s] = uint8(index)
76	c.prog.strings = append(c.prog.strings, s)
77	return uint8(index)
78}
79
80func (c *compiler) internIface(n ast.Node, v interface{}) uint8 {
81	if index, ok := c.ifaceIndexes[v]; ok {
82		return index
83	}
84	index := len(c.prog.ifaces)
85	if !fitsUint8(index) {
86		panic(c.errorf(n, "implementation limitation: too many values"))
87	}
88	c.ifaceIndexes[v] = uint8(index)
89	c.prog.ifaces = append(c.prog.ifaces, v)
90	return uint8(index)
91}
92
93func (c *compiler) emitInst(inst instruction) {
94	c.prog.insts = append(c.prog.insts, inst)
95}
96
97func (c *compiler) emitInstOp(op operation) {
98	c.emitInst(instruction{op: op})
99}
100
101func (c *compiler) compileNode(n ast.Node) {
102	switch n := n.(type) {
103	case *ast.File:
104		c.compileFile(n)
105	case ast.Decl:
106		c.compileDecl(n)
107	case ast.Expr:
108		c.compileExpr(n)
109	case ast.Stmt:
110		c.compileStmt(n)
111	case *ast.ValueSpec:
112		c.compileValueSpec(n)
113	case stmtSlice:
114		c.compileStmtSlice(n)
115	case exprSlice:
116		c.compileExprSlice(n)
117	default:
118		panic(c.errorf(n, "compileNode: unexpected %T", n))
119	}
120}
121
122func (c *compiler) compileOptStmt(n ast.Stmt) {
123	if exprStmt, ok := n.(*ast.ExprStmt); ok {
124		if ident, ok := exprStmt.X.(*ast.Ident); ok && isWildName(ident.Name) {
125			c.compileWildIdent(ident, true)
126			return
127		}
128	}
129	c.compileStmt(n)
130}
131
132func (c *compiler) compileOptExpr(n ast.Expr) {
133	if ident, ok := n.(*ast.Ident); ok && isWildName(ident.Name) {
134		c.compileWildIdent(ident, true)
135		return
136	}
137	c.compileExpr(n)
138}
139
140func (c *compiler) compileFieldList(n *ast.FieldList) {
141	c.emitInstOp(opFieldList)
142	for _, x := range n.List {
143		c.compileField(x)
144	}
145	c.emitInstOp(opEnd)
146}
147
148func (c *compiler) compileField(n *ast.Field) {
149	switch {
150	case len(n.Names) == 0:
151		c.emitInstOp(opUnnamedField)
152	case len(n.Names) == 1:
153		name := n.Names[0]
154		if isWildName(name.Name) {
155			c.emitInstOp(opField)
156			c.compileWildIdent(name, false)
157		} else {
158			c.emitInst(instruction{
159				op:         opSimpleField,
160				valueIndex: c.internString(name, name.Name),
161			})
162		}
163	default:
164		c.emitInstOp(opMultiField)
165		for _, name := range n.Names {
166			c.compileIdent(name)
167		}
168		c.emitInstOp(opEnd)
169	}
170	c.compileExpr(n.Type)
171}
172
173func (c *compiler) compileValueSpec(spec *ast.ValueSpec) {
174	switch {
175	case spec.Type == nil:
176		c.emitInstOp(opValueInitSpec)
177	case len(spec.Values) == 0:
178		c.emitInstOp(opTypedValueSpec)
179	default:
180		c.emitInstOp(opTypedValueInitSpec)
181	}
182	for _, name := range spec.Names {
183		c.compileIdent(name)
184	}
185	c.emitInstOp(opEnd)
186	if spec.Type != nil {
187		c.compileExpr(spec.Type)
188	}
189	if len(spec.Values) != 0 {
190		for _, v := range spec.Values {
191			c.compileExpr(v)
192		}
193		c.emitInstOp(opEnd)
194	}
195}
196
197func (c *compiler) compileTypeSpec(spec *ast.TypeSpec) {
198	c.emitInstOp(pickOp(spec.Assign.IsValid(), opTypeAliasSpec, opTypeSpec))
199	c.compileIdent(spec.Name)
200	c.compileExpr(spec.Type)
201}
202
203func (c *compiler) compileFile(n *ast.File) {
204	if len(n.Imports) == 0 && len(n.Decls) == 0 {
205		c.emitInstOp(opEmptyPackage)
206		c.compileIdent(n.Name)
207		return
208	}
209
210	panic(c.errorf(n, "compileFile: unsupported file pattern"))
211}
212
213func (c *compiler) compileDecl(n ast.Decl) {
214	switch n := n.(type) {
215	case *ast.FuncDecl:
216		c.compileFuncDecl(n)
217	case *ast.GenDecl:
218		c.compileGenDecl(n)
219
220	default:
221		panic(c.errorf(n, "compileDecl: unexpected %T", n))
222	}
223}
224
225func (c *compiler) compileFuncDecl(n *ast.FuncDecl) {
226	if n.Recv == nil {
227		c.emitInstOp(pickOp(n.Body == nil, opFuncProtoDecl, opFuncDecl))
228	} else {
229		c.emitInstOp(pickOp(n.Body == nil, opMethodProtoDecl, opMethodDecl))
230	}
231
232	if n.Recv != nil {
233		c.compileFieldList(n.Recv)
234	}
235	c.compileIdent(n.Name)
236	c.compileFuncType(n.Type)
237	if n.Body != nil {
238		c.compileBlockStmt(n.Body)
239	}
240}
241
242func (c *compiler) compileGenDecl(n *ast.GenDecl) {
243	switch n.Tok {
244	case token.CONST, token.VAR:
245		c.emitInstOp(pickOp(n.Tok == token.CONST, opConstDecl, opVarDecl))
246		for _, spec := range n.Specs {
247			c.compileValueSpec(spec.(*ast.ValueSpec))
248		}
249		c.emitInstOp(opEnd)
250	case token.TYPE:
251		c.emitInstOp(opTypeDecl)
252		for _, spec := range n.Specs {
253			c.compileTypeSpec(spec.(*ast.TypeSpec))
254		}
255		c.emitInstOp(opEnd)
256
257	default:
258		panic(c.errorf(n, "unexpected gen decl"))
259	}
260}
261
262func (c *compiler) compileExpr(n ast.Expr) {
263	switch n := n.(type) {
264	case *ast.BasicLit:
265		c.compileBasicLit(n)
266	case *ast.BinaryExpr:
267		c.compileBinaryExpr(n)
268	case *ast.IndexExpr:
269		c.compileIndexExpr(n)
270	case *ast.Ident:
271		c.compileIdent(n)
272	case *ast.CallExpr:
273		c.compileCallExpr(n)
274	case *ast.UnaryExpr:
275		c.compileUnaryExpr(n)
276	case *ast.StarExpr:
277		c.compileStarExpr(n)
278	case *ast.ParenExpr:
279		c.compileParenExpr(n)
280	case *ast.SliceExpr:
281		c.compileSliceExpr(n)
282	case *ast.FuncType:
283		c.compileFuncType(n)
284	case *ast.ArrayType:
285		c.compileArrayType(n)
286	case *ast.MapType:
287		c.compileMapType(n)
288	case *ast.ChanType:
289		c.compileChanType(n)
290	case *ast.CompositeLit:
291		c.compileCompositeLit(n)
292	case *ast.FuncLit:
293		c.compileFuncLit(n)
294	case *ast.Ellipsis:
295		c.compileEllipsis(n)
296	case *ast.KeyValueExpr:
297		c.compileKeyValueExpr(n)
298	case *ast.SelectorExpr:
299		c.compileSelectorExpr(n)
300	case *ast.TypeAssertExpr:
301		c.compileTypeAssertExpr(n)
302
303	default:
304		panic(c.errorf(n, "compileExpr: unexpected %T", n))
305	}
306}
307
308func (c *compiler) compileBasicLit(n *ast.BasicLit) {
309	if !c.strict {
310		v := literalValue(n)
311		if v == nil {
312			panic(c.errorf(n, "can't convert %s (%s) value", n.Value, n.Kind))
313		}
314		c.prog.insts = append(c.prog.insts, instruction{
315			op:         opBasicLit,
316			valueIndex: c.internIface(n, v),
317		})
318		return
319	}
320
321	var inst instruction
322	switch n.Kind {
323	case token.INT:
324		inst.op = opStrictIntLit
325	case token.FLOAT:
326		inst.op = opStrictFloatLit
327	case token.STRING:
328		inst.op = opStrictStringLit
329	case token.CHAR:
330		inst.op = opStrictCharLit
331	default:
332		inst.op = opStrictComplexLit
333	}
334	inst.valueIndex = c.internString(n, n.Value)
335	c.prog.insts = append(c.prog.insts, inst)
336}
337
338func (c *compiler) compileBinaryExpr(n *ast.BinaryExpr) {
339	c.prog.insts = append(c.prog.insts, instruction{
340		op:    opBinaryExpr,
341		value: c.toUint8(n, int(n.Op)),
342	})
343	c.compileExpr(n.X)
344	c.compileExpr(n.Y)
345}
346
347func (c *compiler) compileIndexExpr(n *ast.IndexExpr) {
348	c.emitInstOp(opIndexExpr)
349	c.compileExpr(n.X)
350	c.compileExpr(n.Index)
351}
352
353func (c *compiler) compileWildIdent(n *ast.Ident, optional bool) {
354	info := decodeWildName(n.Name)
355	var inst instruction
356	switch {
357	case info.Name == "_" && !info.Seq:
358		inst.op = opNode
359	case info.Name == "_" && info.Seq:
360		inst.op = pickOp(optional, opOptNode, opNodeSeq)
361	case info.Name != "_" && !info.Seq:
362		inst.op = opNamedNode
363		inst.valueIndex = c.internString(n, info.Name)
364	default:
365		inst.op = pickOp(optional, opNamedOptNode, opNamedNodeSeq)
366		inst.valueIndex = c.internString(n, info.Name)
367	}
368	c.prog.insts = append(c.prog.insts, inst)
369}
370
371func (c *compiler) compileIdent(n *ast.Ident) {
372	if isWildName(n.Name) {
373		c.compileWildIdent(n, false)
374		return
375	}
376
377	c.prog.insts = append(c.prog.insts, instruction{
378		op:         opIdent,
379		valueIndex: c.internString(n, n.Name),
380	})
381}
382
383func (c *compiler) compileCallExpr(n *ast.CallExpr) {
384	op := opCallExpr
385	if n.Ellipsis.IsValid() {
386		op = opVariadicCallExpr
387	}
388	c.emitInstOp(op)
389	c.compileExpr(n.Fun)
390	for _, arg := range n.Args {
391		c.compileExpr(arg)
392	}
393	c.emitInstOp(opEnd)
394}
395
396func (c *compiler) compileUnaryExpr(n *ast.UnaryExpr) {
397	c.prog.insts = append(c.prog.insts, instruction{
398		op:    opUnaryExpr,
399		value: c.toUint8(n, int(n.Op)),
400	})
401	c.compileExpr(n.X)
402}
403
404func (c *compiler) compileStarExpr(n *ast.StarExpr) {
405	c.emitInstOp(opStarExpr)
406	c.compileExpr(n.X)
407}
408
409func (c *compiler) compileParenExpr(n *ast.ParenExpr) {
410	c.emitInstOp(opParenExpr)
411	c.compileExpr(n.X)
412}
413
414func (c *compiler) compileSliceExpr(n *ast.SliceExpr) {
415	switch {
416	case n.Low == nil && n.High == nil && !n.Slice3:
417		c.emitInstOp(opSliceExpr)
418		c.compileExpr(n.X)
419	case n.Low != nil && n.High == nil && !n.Slice3:
420		c.emitInstOp(opSliceFromExpr)
421		c.compileExpr(n.X)
422		c.compileExpr(n.Low)
423	case n.Low == nil && n.High != nil && !n.Slice3:
424		c.emitInstOp(opSliceToExpr)
425		c.compileExpr(n.X)
426		c.compileExpr(n.High)
427	case n.Low != nil && n.High != nil && !n.Slice3:
428		c.emitInstOp(opSliceFromToExpr)
429		c.compileExpr(n.X)
430		c.compileExpr(n.Low)
431		c.compileExpr(n.High)
432	case n.Low == nil && n.Slice3:
433		c.emitInstOp(opSliceToCapExpr)
434		c.compileExpr(n.X)
435		c.compileExpr(n.High)
436		c.compileExpr(n.Max)
437	case n.Low != nil && n.Slice3:
438		c.emitInstOp(opSliceFromToCapExpr)
439		c.compileExpr(n.X)
440		c.compileExpr(n.Low)
441		c.compileExpr(n.High)
442		c.compileExpr(n.Max)
443	default:
444		panic(c.errorf(n, "unexpected slice expr"))
445	}
446}
447
448func (c *compiler) compileFuncType(n *ast.FuncType) {
449	void := n.Results == nil || len(n.Results.List) == 0
450	if void {
451		c.emitInstOp(opVoidFuncType)
452	} else {
453		c.emitInstOp(opFuncType)
454	}
455	c.compileFieldList(n.Params)
456	if !void {
457		c.compileFieldList(n.Results)
458	}
459}
460
461func (c *compiler) compileArrayType(n *ast.ArrayType) {
462	if n.Len == nil {
463		c.emitInstOp(opSliceType)
464		c.compileExpr(n.Elt)
465	} else {
466		c.emitInstOp(opArrayType)
467		c.compileExpr(n.Len)
468		c.compileExpr(n.Elt)
469	}
470}
471
472func (c *compiler) compileMapType(n *ast.MapType) {
473	c.emitInstOp(opMapType)
474	c.compileExpr(n.Key)
475	c.compileExpr(n.Value)
476}
477
478func (c *compiler) compileChanType(n *ast.ChanType) {
479	c.emitInst(instruction{
480		op:    opChanType,
481		value: c.toUint8(n, int(n.Dir)),
482	})
483	c.compileExpr(n.Value)
484}
485
486func (c *compiler) compileCompositeLit(n *ast.CompositeLit) {
487	if n.Type == nil {
488		c.emitInstOp(opCompositeLit)
489	} else {
490		c.emitInstOp(opTypedCompositeLit)
491		c.compileExpr(n.Type)
492	}
493	for _, elt := range n.Elts {
494		c.compileExpr(elt)
495	}
496	c.emitInstOp(opEnd)
497}
498
499func (c *compiler) compileFuncLit(n *ast.FuncLit) {
500	c.emitInstOp(opFuncLit)
501	c.compileFuncType(n.Type)
502	c.compileBlockStmt(n.Body)
503}
504
505func (c *compiler) compileEllipsis(n *ast.Ellipsis) {
506	if n.Elt == nil {
507		c.emitInstOp(opEllipsis)
508	} else {
509		c.emitInstOp(opTypedEllipsis)
510		c.compileExpr(n.Elt)
511	}
512}
513
514func (c *compiler) compileKeyValueExpr(n *ast.KeyValueExpr) {
515	c.emitInstOp(opKeyValueExpr)
516	c.compileExpr(n.Key)
517	c.compileExpr(n.Value)
518}
519
520func (c *compiler) compileSelectorExpr(n *ast.SelectorExpr) {
521	if isWildName(n.Sel.Name) {
522		c.emitInstOp(opSelectorExpr)
523		c.compileWildIdent(n.Sel, false)
524		c.compileExpr(n.X)
525		return
526	}
527
528	c.prog.insts = append(c.prog.insts, instruction{
529		op:         opSimpleSelectorExpr,
530		valueIndex: c.internString(n.Sel, n.Sel.String()),
531	})
532	c.compileExpr(n.X)
533}
534
535func (c *compiler) compileTypeAssertExpr(n *ast.TypeAssertExpr) {
536	if n.Type != nil {
537		c.emitInstOp(opTypeAssertExpr)
538		c.compileExpr(n.X)
539		c.compileExpr(n.Type)
540	} else {
541		c.emitInstOp(opTypeSwitchAssertExpr)
542		c.compileExpr(n.X)
543	}
544}
545
546func (c *compiler) compileStmt(n ast.Stmt) {
547	switch n := n.(type) {
548	case *ast.AssignStmt:
549		c.compileAssignStmt(n)
550	case *ast.BlockStmt:
551		c.compileBlockStmt(n)
552	case *ast.ExprStmt:
553		c.compileExprStmt(n)
554	case *ast.IfStmt:
555		c.compileIfStmt(n)
556	case *ast.CaseClause:
557		c.compileCaseClause(n)
558	case *ast.SwitchStmt:
559		c.compileSwitchStmt(n)
560	case *ast.TypeSwitchStmt:
561		c.compileTypeSwitchStmt(n)
562	case *ast.SelectStmt:
563		c.compileSelectStmt(n)
564	case *ast.ForStmt:
565		c.compileForStmt(n)
566	case *ast.RangeStmt:
567		c.compileRangeStmt(n)
568	case *ast.IncDecStmt:
569		c.compileIncDecStmt(n)
570	case *ast.EmptyStmt:
571		c.compileEmptyStmt(n)
572	case *ast.ReturnStmt:
573		c.compileReturnStmt(n)
574	case *ast.BranchStmt:
575		c.compileBranchStmt(n)
576	case *ast.LabeledStmt:
577		c.compileLabeledStmt(n)
578	case *ast.GoStmt:
579		c.compileGoStmt(n)
580	case *ast.DeferStmt:
581		c.compileDeferStmt(n)
582	case *ast.SendStmt:
583		c.compileSendStmt(n)
584	case *ast.DeclStmt:
585		c.compileDecl(n.Decl)
586
587	default:
588		panic(c.errorf(n, "compileStmt: unexpected %T", n))
589	}
590}
591
592func (c *compiler) compileAssignStmt(n *ast.AssignStmt) {
593	if len(n.Lhs) == 1 && len(n.Rhs) == 1 {
594		lhsInfo := decodeWildNode(n.Lhs[0])
595		rhsInfo := decodeWildNode(n.Rhs[0])
596		if !lhsInfo.Seq && !rhsInfo.Seq {
597			c.emitInst(instruction{
598				op:    opAssignStmt,
599				value: uint8(n.Tok),
600			})
601			c.compileExpr(n.Lhs[0])
602			c.compileExpr(n.Rhs[0])
603			return
604		}
605	}
606
607	c.emitInst(instruction{
608		op:    opMultiAssignStmt,
609		value: uint8(n.Tok),
610	})
611	for _, x := range n.Lhs {
612		c.compileExpr(x)
613	}
614	c.emitInstOp(opEnd)
615	for _, x := range n.Rhs {
616		c.compileExpr(x)
617	}
618	c.emitInstOp(opEnd)
619}
620
621func (c *compiler) compileBlockStmt(n *ast.BlockStmt) {
622	c.emitInstOp(opBlockStmt)
623	for _, elt := range n.List {
624		c.compileStmt(elt)
625	}
626	c.emitInstOp(opEnd)
627}
628
629func (c *compiler) compileExprStmt(n *ast.ExprStmt) {
630	if ident, ok := n.X.(*ast.Ident); ok && isWildName(ident.Name) {
631		c.compileIdent(ident)
632	} else {
633		c.emitInstOp(opExprStmt)
634		c.compileExpr(n.X)
635	}
636}
637
638func (c *compiler) compileIfStmt(n *ast.IfStmt) {
639	// Check for the special case: `if $*_ ...` should match all if statements.
640	if ident, ok := n.Cond.(*ast.Ident); ok && n.Init == nil && isWildName(ident.Name) {
641		info := decodeWildName(ident.Name)
642		if info.Seq && info.Name == "_" {
643			// Set Init to Cond, change cond from $*_ to $_.
644			n.Init = &ast.ExprStmt{X: n.Cond}
645			cond := &ast.Ident{Name: encodeWildName(info.Name, false)}
646			n.Cond = cond
647			c.compileIfStmt(n)
648			return
649		}
650		// Named $* is harder and slower.
651		c.prog.insts = append(c.prog.insts, instruction{
652			op:         pickOp(n.Else == nil, opIfNamedOptStmt, opIfNamedOptElseStmt),
653			valueIndex: c.internString(ident, info.Name),
654		})
655		c.compileStmt(n.Body)
656		if n.Else != nil {
657			c.compileStmt(n.Else)
658		}
659		return
660	}
661
662	switch {
663	case n.Init == nil && n.Else == nil:
664		c.emitInstOp(opIfStmt)
665		c.compileExpr(n.Cond)
666		c.compileStmt(n.Body)
667	case n.Init != nil && n.Else == nil:
668		c.emitInstOp(opIfInitStmt)
669		c.compileOptStmt(n.Init)
670		c.compileExpr(n.Cond)
671		c.compileStmt(n.Body)
672	case n.Init == nil && n.Else != nil:
673		c.emitInstOp(opIfElseStmt)
674		c.compileExpr(n.Cond)
675		c.compileStmt(n.Body)
676		c.compileStmt(n.Else)
677	case n.Init != nil && n.Else != nil:
678		c.emitInstOp(opIfInitElseStmt)
679		c.compileOptStmt(n.Init)
680		c.compileExpr(n.Cond)
681		c.compileStmt(n.Body)
682		c.compileStmt(n.Else)
683
684	default:
685		panic(c.errorf(n, "unexpected if stmt"))
686	}
687}
688
689func (c *compiler) compileCommClause(n *ast.CommClause) {
690	c.emitInstOp(pickOp(n.Comm == nil, opDefaultCommClause, opCommClause))
691	if n.Comm != nil {
692		c.compileStmt(n.Comm)
693	}
694	for _, x := range n.Body {
695		c.compileStmt(x)
696	}
697	c.emitInstOp(opEnd)
698}
699
700func (c *compiler) compileCaseClause(n *ast.CaseClause) {
701	c.emitInstOp(pickOp(n.List == nil, opDefaultCaseClause, opCaseClause))
702	if n.List != nil {
703		for _, x := range n.List {
704			c.compileExpr(x)
705		}
706		c.emitInstOp(opEnd)
707	}
708	for _, x := range n.Body {
709		c.compileStmt(x)
710	}
711	c.emitInstOp(opEnd)
712}
713
714func (c *compiler) compileSwitchBody(n *ast.BlockStmt) {
715	wildcardCase := func(cc *ast.CaseClause) *ast.Ident {
716		if len(cc.List) != 1 || len(cc.Body) != 1 {
717			return nil
718		}
719		v, ok := cc.List[0].(*ast.Ident)
720		if !ok || !isWildName(v.Name) {
721			return nil
722		}
723		bodyStmt, ok := cc.Body[0].(*ast.ExprStmt)
724		if !ok {
725			return nil
726		}
727		bodyIdent, ok := bodyStmt.X.(*ast.Ident)
728		if !ok || bodyIdent.Name != "gogrep_body" {
729			return nil
730		}
731		return v
732	}
733	for _, cc := range n.List {
734		cc := cc.(*ast.CaseClause)
735		wildcard := wildcardCase(cc)
736		if wildcard == nil {
737			c.compileCaseClause(cc)
738			continue
739		}
740		c.compileWildIdent(wildcard, false)
741	}
742	c.emitInstOp(opEnd)
743}
744
745func (c *compiler) compileSwitchStmt(n *ast.SwitchStmt) {
746	var op operation
747	switch {
748	case n.Init == nil && n.Tag == nil:
749		op = opSwitchStmt
750	case n.Init == nil && n.Tag != nil:
751		op = opSwitchTagStmt
752	case n.Init != nil && n.Tag == nil:
753		op = opSwitchInitStmt
754	default:
755		op = opSwitchInitTagStmt
756	}
757
758	c.emitInstOp(op)
759	if n.Init != nil {
760		c.compileOptStmt(n.Init)
761	}
762	if n.Tag != nil {
763		c.compileOptExpr(n.Tag)
764	}
765	c.compileSwitchBody(n.Body)
766}
767
768func (c *compiler) compileTypeSwitchStmt(n *ast.TypeSwitchStmt) {
769	c.emitInstOp(pickOp(n.Init == nil, opTypeSwitchStmt, opTypeSwitchInitStmt))
770	if n.Init != nil {
771		c.compileOptStmt(n.Init)
772	}
773	c.compileStmt(n.Assign)
774	c.compileSwitchBody(n.Body)
775}
776
777func (c *compiler) compileSelectStmt(n *ast.SelectStmt) {
778	c.emitInstOp(opSelectStmt)
779
780	wildcardCase := func(cc *ast.CommClause) *ast.Ident {
781		if cc.Comm == nil {
782			return nil
783		}
784		vStmt, ok := cc.Comm.(*ast.ExprStmt)
785		if !ok {
786			return nil
787		}
788		v, ok := vStmt.X.(*ast.Ident)
789		if !ok || !isWildName(v.Name) {
790			return nil
791		}
792		bodyStmt, ok := cc.Body[0].(*ast.ExprStmt)
793		if !ok {
794			return nil
795		}
796		bodyIdent, ok := bodyStmt.X.(*ast.Ident)
797		if !ok || bodyIdent.Name != "gogrep_body" {
798			return nil
799		}
800		return v
801	}
802	for _, cc := range n.Body.List {
803		cc := cc.(*ast.CommClause)
804		wildcard := wildcardCase(cc)
805		if wildcard == nil {
806			c.compileCommClause(cc)
807			continue
808		}
809		c.compileWildIdent(wildcard, false)
810	}
811	c.emitInstOp(opEnd)
812}
813
814func (c *compiler) compileForStmt(n *ast.ForStmt) {
815	var op operation
816	switch {
817	case n.Init == nil && n.Cond == nil && n.Post == nil:
818		op = opForStmt
819	case n.Init == nil && n.Cond == nil && n.Post != nil:
820		op = opForPostStmt
821	case n.Init == nil && n.Cond != nil && n.Post == nil:
822		op = opForCondStmt
823	case n.Init == nil && n.Cond != nil && n.Post != nil:
824		op = opForCondPostStmt
825	case n.Init != nil && n.Cond == nil && n.Post == nil:
826		op = opForInitStmt
827	case n.Init != nil && n.Cond == nil && n.Post != nil:
828		op = opForInitPostStmt
829	case n.Init != nil && n.Cond != nil && n.Post == nil:
830		op = opForInitCondStmt
831	default:
832		op = opForInitCondPostStmt
833	}
834
835	c.emitInstOp(op)
836	if n.Init != nil {
837		c.compileOptStmt(n.Init)
838	}
839	if n.Cond != nil {
840		c.compileOptExpr(n.Cond)
841	}
842	if n.Post != nil {
843		c.compileOptStmt(n.Post)
844	}
845	c.compileBlockStmt(n.Body)
846}
847
848func (c *compiler) compileRangeStmt(n *ast.RangeStmt) {
849	switch {
850	case n.Key == nil && n.Value == nil:
851		c.emitInstOp(opRangeStmt)
852		c.compileExpr(n.X)
853		c.compileStmt(n.Body)
854	case n.Key != nil && n.Value == nil:
855		c.emitInst(instruction{
856			op:    opRangeKeyStmt,
857			value: c.toUint8(n, int(n.Tok)),
858		})
859		c.compileExpr(n.Key)
860		c.compileExpr(n.X)
861		c.compileStmt(n.Body)
862	case n.Key != nil && n.Value != nil:
863		c.emitInst(instruction{
864			op:    opRangeKeyValueStmt,
865			value: c.toUint8(n, int(n.Tok)),
866		})
867		c.compileExpr(n.Key)
868		c.compileExpr(n.Value)
869		c.compileExpr(n.X)
870		c.compileStmt(n.Body)
871	default:
872		panic(c.errorf(n, "unexpected range stmt"))
873	}
874}
875
876func (c *compiler) compileIncDecStmt(n *ast.IncDecStmt) {
877	c.prog.insts = append(c.prog.insts, instruction{
878		op:    opIncDecStmt,
879		value: c.toUint8(n, int(n.Tok)),
880	})
881	c.compileExpr(n.X)
882}
883
884func (c *compiler) compileEmptyStmt(n *ast.EmptyStmt) {
885	_ = n // unused
886	c.emitInstOp(opEmptyStmt)
887}
888
889func (c *compiler) compileReturnStmt(n *ast.ReturnStmt) {
890	c.emitInstOp(opReturnStmt)
891	for _, x := range n.Results {
892		c.compileExpr(x)
893	}
894	c.emitInstOp(opEnd)
895}
896
897func (c *compiler) compileBranchStmt(n *ast.BranchStmt) {
898	if n.Label != nil {
899		if isWildName(n.Label.Name) {
900			c.prog.insts = append(c.prog.insts, instruction{
901				op:    opLabeledBranchStmt,
902				value: c.toUint8(n, int(n.Tok)),
903			})
904			c.compileWildIdent(n.Label, false)
905		} else {
906			c.prog.insts = append(c.prog.insts, instruction{
907				op:         opSimpleLabeledBranchStmt,
908				value:      c.toUint8(n, int(n.Tok)),
909				valueIndex: c.internString(n.Label, n.Label.Name),
910			})
911		}
912		return
913	}
914	c.prog.insts = append(c.prog.insts, instruction{
915		op:    opBranchStmt,
916		value: c.toUint8(n, int(n.Tok)),
917	})
918}
919
920func (c *compiler) compileLabeledStmt(n *ast.LabeledStmt) {
921	if isWildName(n.Label.Name) {
922		c.emitInstOp(opLabeledStmt)
923		c.compileWildIdent(n.Label, false)
924		c.compileStmt(n.Stmt)
925		return
926	}
927
928	c.prog.insts = append(c.prog.insts, instruction{
929		op:         opSimpleLabeledStmt,
930		valueIndex: c.internString(n.Label, n.Label.Name),
931	})
932	c.compileStmt(n.Stmt)
933}
934
935func (c *compiler) compileGoStmt(n *ast.GoStmt) {
936	c.emitInstOp(opGoStmt)
937	c.compileExpr(n.Call)
938}
939
940func (c *compiler) compileDeferStmt(n *ast.DeferStmt) {
941	c.emitInstOp(opDeferStmt)
942	c.compileExpr(n.Call)
943}
944
945func (c *compiler) compileSendStmt(n *ast.SendStmt) {
946	c.emitInstOp(opSendStmt)
947	c.compileExpr(n.Chan)
948	c.compileExpr(n.Value)
949}
950
951func (c *compiler) compileStmtSlice(stmts stmtSlice) {
952	c.emitInstOp(opMultiStmt)
953	for _, n := range stmts {
954		c.compileStmt(n)
955	}
956	c.emitInstOp(opEnd)
957}
958
959func (c *compiler) compileExprSlice(exprs exprSlice) {
960	c.emitInstOp(opMultiExpr)
961	for _, n := range exprs {
962		c.compileExpr(n)
963	}
964	c.emitInstOp(opEnd)
965}
966
967func pickOp(cond bool, ifTrue, ifFalse operation) operation {
968	if cond {
969		return ifTrue
970	}
971	return ifFalse
972}
973
974func fitsUint8(v int) bool {
975	return v >= 0 && v <= 0xff
976}
977