func (pcomp *pcomp) functionIndex(fn *Funcode) uint32 {
	index, ok := pcomp.functions[fn]
	if !ok {
		index = uint32(len(pcomp.prog.Functions))
		pcomp.functions[fn] = index
		pcomp.prog.Functions = append(pcomp.prog.Functions, fn)
	}
	return index
}
functionIndex returns the index of the specified function
AST the nestedfun pool, adding it if necessary.
package compile
import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"github.com/google/skylark/resolve"
	"github.com/google/skylark/syntax"
)
const debug = false  
const Version = 3
type Opcode uint8
const (
	NOP Opcode = iota  
	DUP   
	DUP2  
	POP   
	EXCH  
	LT
	GT
	GE
	LE
	EQL
	NEQ
	PLUS
	MINUS
	STAR
	SLASH
	SLASHSLASH
	PERCENT
	AMP
	PIPE
	CIRCUMFLEX
	LTLT
	GTGT
	IN
	UPLUS   
	UMINUS  
	TILDE   
	NONE   
	TRUE   
	FALSE  
	ITERPUSH     
	ITERPOP      
	NOT          
	RETURN       
	SETINDEX     
	INDEX        
	SETDICT      
	SETDICTUNIQ  
	APPEND       
	SLICE        
	INPLACE_ADD  
	MAKEDICT     
	JMP      
	CJMP     
	ITERJMP  
	CONSTANT     
	MAKETUPLE    
	MAKELIST     
	MAKEFUNC     
	LOAD         
	SETLOCAL     
	SETGLOBAL    
	LOCAL        
	FREE         
	GLOBAL       
	PREDECLARED  
	UNIVERSAL    
	ATTR         
	SETFIELD     
	UNPACK       
	CALL         
	CALL_VAR     
	CALL_KW      
	CALL_VAR_KW  
	OpcodeArgMin = JMP
	OpcodeMax    = CALL_VAR_KW
)
var opcodeNames = [...]string{
	AMP:         "amp",
	APPEND:      "append",
	ATTR:        "attr",
	CALL:        "call",
	CALL_KW:     "call_kw ",
	CALL_VAR:    "call_var",
	CALL_VAR_KW: "call_var_kw",
	CIRCUMFLEX:  "circumflex",
	CJMP:        "cjmp",
	CONSTANT:    "constant",
	DUP2:        "dup2",
	DUP:         "dup",
	EQL:         "eql",
	FALSE:       "false",
	FREE:        "free",
	GE:          "ge",
	GLOBAL:      "global",
	GT:          "gt",
	GTGT:        "gtgt",
	IN:          "in",
	INDEX:       "index",
	INPLACE_ADD: "inplace_add",
	ITERJMP:     "iterjmp",
	ITERPOP:     "iterpop",
	ITERPUSH:    "iterpush",
	JMP:         "jmp",
	LE:          "le",
	LOAD:        "load",
	LOCAL:       "local",
	LT:          "lt",
	LTLT:        "ltlt",
	MAKEDICT:    "makedict",
	MAKEFUNC:    "makefunc",
	MAKELIST:    "makelist",
	MAKETUPLE:   "maketuple",
	MINUS:       "minus",
	NEQ:         "neq",
	NONE:        "none",
	NOP:         "nop",
	NOT:         "not",
	PERCENT:     "percent",
	PIPE:        "pipe",
	PLUS:        "plus",
	POP:         "pop",
	PREDECLARED: "predeclared",
	RETURN:      "return",
	SETDICT:     "setdict",
	SETDICTUNIQ: "setdictuniq",
	SETFIELD:    "setfield",
	SETGLOBAL:   "setglobal",
	SETINDEX:    "setindex",
	SETLOCAL:    "setlocal",
	SLASH:       "slash",
	SLASHSLASH:  "slashslash",
	SLICE:       "slice",
	STAR:        "star",
	TILDE:       "tilde",
	TRUE:        "true",
	UMINUS:      "uminus",
	UNIVERSAL:   "universal",
	UNPACK:      "unpack",
	UPLUS:       "uplus",
}
const variableStackEffect = 0x7f
var stackEffect = [...]int8{
	AMP:         -1,
	APPEND:      -2,
	ATTR:        0,
	CALL:        variableStackEffect,
	CALL_KW:     variableStackEffect,
	CALL_VAR:    variableStackEffect,
	CALL_VAR_KW: variableStackEffect,
	CIRCUMFLEX:  -1,
	CJMP:        -1,
	CONSTANT:    +1,
	DUP2:        +2,
	DUP:         +1,
	EQL:         -1,
	FALSE:       +1,
	FREE:        +1,
	GE:          -1,
	GLOBAL:      +1,
	GT:          -1,
	GTGT:        -1,
	IN:          -1,
	INDEX:       -1,
	INPLACE_ADD: -1,
	ITERJMP:     variableStackEffect,
	ITERPOP:     0,
	ITERPUSH:    -1,
	JMP:         0,
	LE:          -1,
	LOAD:        -1,
	LOCAL:       +1,
	LT:          -1,
	LTLT:        -1,
	MAKEDICT:    +1,
	MAKEFUNC:    -1,
	MAKELIST:    variableStackEffect,
	MAKETUPLE:   variableStackEffect,
	MINUS:       -1,
	NEQ:         -1,
	NONE:        +1,
	NOP:         0,
	NOT:         0,
	PERCENT:     -1,
	PIPE:        -1,
	PLUS:        -1,
	POP:         -1,
	PREDECLARED: +1,
	RETURN:      -1,
	SETDICT:     -3,
	SETDICTUNIQ: -3,
	SETFIELD:    -2,
	SETGLOBAL:   -1,
	SETINDEX:    -3,
	SETLOCAL:    -1,
	SLASH:       -1,
	SLASHSLASH:  -1,
	SLICE:       -3,
	STAR:        -1,
	TRUE:        +1,
	UNIVERSAL:   +1,
	UNPACK:      variableStackEffect,
}
func (op Opcode) String() string {
	if op < OpcodeMax {
		return opcodeNames[op]
	}
	return fmt.Sprintf("illegal op (%d)", op)
}
type Program struct {
	Loads     []Ident        
	Names     []string       
	Constants []interface{}  
	Functions []*Funcode
	Globals   []Ident   
	Toplevel  *Funcode  
}
type Funcode struct {
	Prog                  *Program
	Pos                   syntax.Position  
	Name                  string           
	Code                  []byte           
	pclinetab             []uint16         
	Locals                []Ident          
	Freevars              []Ident          
	MaxStack              int
	NumParams             int
	HasVarargs, HasKwargs bool
}
type Ident struct {
	Name string
	Pos  syntax.Position
}
type pcomp struct {
	prog *Program  
	names     map[string]uint32
	constants map[interface{}]uint32
	functions map[*Funcode]uint32
}
type fcomp struct {
	fn *Funcode  
	pcomp *pcomp
	pos   syntax.Position  
	loops []loop
	block *block
}
type loop struct {
	break_, continue_ *block
}
type block struct {
	insns []insn
	jmp, cjmp *block
	initialstack int  
	index int  
	addr  uint32
}
type insn struct {
	op   Opcode
	arg  uint32
	line int32
}
func (fn *Funcode) Position(pc uint32) syntax.Position {
	pos := fn.Pos  
	pos.Line = 0
	pos.Col = 0
	var prevpc uint32
	complete := true
	for _, x := range fn.pclinetab {
		nextpc := prevpc + uint32(x>>8)
		if complete && nextpc > pc {
			return pos
		}
		prevpc = nextpc
		pos.Line += int32(int8(x) >> 1)  
		complete = (x & 1) == 0
	}
	return pos
}
func idents(ids []*syntax.Ident) []Ident {
	res := make([]Ident, len(ids))
	for i, id := range ids {
		res[i].Name = id.Name
		res[i].Pos = id.NamePos
	}
	return res
}
func Expr(expr syntax.Expr, locals []*syntax.Ident) *Funcode {
	stmts := []syntax.Stmt{&syntax.ReturnStmt{Result: expr}}
	return File(stmts, locals, nil).Toplevel
}
func File(stmts []syntax.Stmt, locals, globals []*syntax.Ident) *Program {
	pcomp := &pcomp{
		prog: &Program{
			Globals: idents(globals),
		},
		names:     make(map[string]uint32),
		constants: make(map[interface{}]uint32),
		functions: make(map[*Funcode]uint32),
	}
	var pos syntax.Position
	if len(stmts) > 0 {
		pos = syntax.Start(stmts[0])
	}
	pcomp.prog.Toplevel = pcomp.function("<toplevel>", pos, stmts, locals, nil)
	return pcomp.prog
}
func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*syntax.Ident) *Funcode {
	fcomp := &fcomp{
		pcomp: pcomp,
		pos:   pos,
		fn: &Funcode{
			Prog:     pcomp.prog,
			Pos:      pos,
			Name:     name,
			Locals:   idents(locals),
			Freevars: idents(freevars),
		},
	}
	if debug {
		fmt.Fprintf(os.Stderr, "start function(%s @ %s)\n", name, pos)
	}
	entry := fcomp.newBlock()
	fcomp.block = entry
	fcomp.stmts(stmts)
	if fcomp.block != nil {
		fcomp.emit(NONE)
		fcomp.emit(RETURN)
	}
	var oops bool  
	setinitialstack := func(b *block, depth int) {
		if b.initialstack == -1 {
			b.initialstack = depth
		} else if b.initialstack != depth {
			fmt.Fprintf(os.Stderr, "%d: setinitialstack: depth mismatch: %d vs %d\n",
				b.index, b.initialstack, depth)
			oops = true
		}
	}
	var pc uint32
	var blocks []*block
	var maxstack int
	var visit func(b *block)
	visit = func(b *block) {
		if b.index >= 0 {
			return  
		}
		b.index = len(blocks)
		b.addr = pc
		blocks = append(blocks, b)
		stack := b.initialstack
		if debug {
			fmt.Fprintf(os.Stderr, "%s block %d: (stack = %d)\n", name, b.index, stack)
		}
		var cjmpAddr *uint32
		var isiterjmp int
		for i, insn := range b.insns {
			pc++
			if insn.op >= OpcodeArgMin {
				switch insn.op {
				case ITERJMP:
					isiterjmp = 1
					fallthrough
				case CJMP:
					cjmpAddr = &b.insns[i].arg
					pc += 4
				default:
					pc += uint32(argLen(insn.arg))
				}
			}
			se := insn.stackeffect()
			if debug {
				fmt.Fprintln(os.Stderr, "\t", insn.op, stack, stack+se)
			}
			stack += se
			if stack < 0 {
				fmt.Fprintf(os.Stderr, "After pc=%d: stack underflow\n", pc)
				oops = true
			}
			if stack+isiterjmp > maxstack {
				maxstack = stack + isiterjmp
			}
		}
		if debug {
			fmt.Fprintf(os.Stderr, "successors of block %d (start=%d):\n",
				b.addr, b.index)
			if b.jmp != nil {
				fmt.Fprintf(os.Stderr, "jmp to %d\n", b.jmp.index)
			}
			if b.cjmp != nil {
				fmt.Fprintf(os.Stderr, "cjmp to %d\n", b.cjmp.index)
			}
		}
		if b.jmp != nil {
			for b.jmp.insns == nil {
				b.jmp = b.jmp.jmp
			}
			setinitialstack(b.jmp, stack+isiterjmp)
			if b.jmp.index < 0 {
				visit(b.jmp)
			} else {
				pc += 5
			}
		}
		if b.cjmp != nil {
			for b.cjmp.insns == nil {
				b.cjmp = b.cjmp.jmp
			}
			setinitialstack(b.cjmp, stack)
			visit(b.cjmp)
			if cjmpAddr != nil {
				*cjmpAddr = b.cjmp.addr
			}
		}
	}
	setinitialstack(entry, 0)
	visit(entry)
	fn := fcomp.fn
	fn.MaxStack = maxstack
	if debug {
		fmt.Fprintf(os.Stderr, "Function %s: (%d blocks, %d bytes)\n", name, len(blocks), pc)
	}
	fcomp.generate(blocks, pc)
	if debug {
		fmt.Fprintf(os.Stderr, "code=%d maxstack=%d\n", fn.Code, fn.MaxStack)
	}
	if oops {
		panic("internal error")
	}
	if debug {
		fmt.Fprintf(os.Stderr, "end function(%s @ %s)\n", name, pos)
	}
	return fn
}
func (insn *insn) stackeffect() int {
	se := int(stackEffect[insn.op])
	if se == variableStackEffect {
		arg := int(insn.arg)
		switch insn.op {
		case CALL, CALL_KW, CALL_VAR, CALL_VAR_KW:
			se = -int(2*(insn.arg&0xff) + insn.arg>>8)
			if insn.op != CALL {
				se--
			}
			if insn.op == CALL_VAR_KW {
				se--
			}
		case ITERJMP:
			se = 0
		case MAKELIST, MAKETUPLE:
			se = 1 - arg
		case UNPACK:
			se = arg - 1
		default:
			panic(insn.op)
		}
	}
	return se
}
func (fcomp *fcomp) generate(blocks []*block, codelen uint32) {
	code := make([]byte, 0, codelen)
	var pclinetab []uint16
	var prev struct {
		pc   uint32
		line int32
	}
	for _, b := range blocks {
		if debug {
			fmt.Fprintf(os.Stderr, "%d:\n", b.index)
		}
		pc := b.addr
		for _, insn := range b.insns {
			if insn.line != 0 {
				for {
					var incomplete uint16
					deltapc := pc - prev.pc
					if deltapc > 0xff {
						deltapc = 0xff
						incomplete = 1
					}
					prev.pc += deltapc
					deltaline := insn.line - prev.line
					if deltaline > 0x3f {
						deltaline = 0x3f
						incomplete = 1
					} else if deltaline < -0x40 {
						deltaline = -0x40
						incomplete = 1
					}
					prev.line += deltaline
					entry := uint16(deltapc<<8) | uint16(uint8(deltaline<<1)) | incomplete
					pclinetab = append(pclinetab, entry)
					if incomplete == 0 {
						break
					}
				}
				if debug {
					fmt.Fprintf(os.Stderr, "\t\t\t\t\t; %s %d\n",
						filepath.Base(fcomp.fn.Pos.Filename()), insn.line)
				}
			}
			if debug {
				PrintOp(fcomp.fn, pc, insn.op, insn.arg)
			}
			code = append(code, byte(insn.op))
			pc++
			if insn.op >= OpcodeArgMin {
				if insn.op == CJMP || insn.op == ITERJMP {
					code = addUint32(code, insn.arg, 4)  
				} else {
					code = addUint32(code, insn.arg, 0)
				}
				pc = uint32(len(code))
			}
		}
		if b.jmp != nil && b.jmp.index != b.index+1 {
			addr := b.jmp.addr
			if debug {
				fmt.Fprintf(os.Stderr, "\t%d\tjmp\t\t%d\t; block %d\n",
					pc, addr, b.jmp.index)
			}
			code = append(code, byte(JMP))
			code = addUint32(code, addr, 4)
		}
	}
	if len(code) != int(codelen) {
		panic("internal error: wrong code length")
	}
	fcomp.fn.pclinetab = pclinetab
	fcomp.fn.Code = code
}
func addUint32(code []byte, x uint32, min int) []byte {
	end := len(code) + min
	for x >= 0x80 {
		code = append(code, byte(x)|0x80)
		x >>= 7
	}
	code = append(code, byte(x))
	for len(code) < end {
		code = append(code, byte(NOP))
	}
	return code
}
func argLen(x uint32) int {
	n := 0
	for x >= 0x80 {
		n++
		x >>= 7
	}
	return n + 1
}
func PrintOp(fn *Funcode, pc uint32, op Opcode, arg uint32) {
	if op < OpcodeArgMin {
		fmt.Fprintf(os.Stderr, "\t%d\t%s\n", pc, op)
		return
	}
	var comment string
	switch op {
	case CONSTANT:
		switch x := fn.Prog.Constants[arg].(type) {
		case string:
			comment = strconv.Quote(x)
		default:
			comment = fmt.Sprint(x)
		}
	case MAKEFUNC:
		comment = fn.Prog.Functions[arg].Name
	case SETLOCAL, LOCAL:
		comment = fn.Locals[arg].Name
	case SETGLOBAL, GLOBAL:
		comment = fn.Prog.Globals[arg].Name
	case ATTR, SETFIELD, PREDECLARED, UNIVERSAL:
		comment = fn.Prog.Names[arg]
	case FREE:
		comment = fn.Freevars[arg].Name
	case CALL, CALL_VAR, CALL_KW, CALL_VAR_KW:
		comment = fmt.Sprintf("%d pos, %d named", arg>>8, arg&0xff)
	default:
	}
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "\t%d\t%-10s\t%d", pc, op, arg)
	if comment != "" {
		fmt.Fprint(&buf, "\t; ", comment)
	}
	fmt.Fprintln(&buf)
	os.Stderr.Write(buf.Bytes())
}
func (fcomp) newBlock() *block {
	return &block{index: -1, initialstack: -1}
}
func (fcomp *fcomp) emit(op Opcode) {
	if op >= OpcodeArgMin {
		panic("missing arg: " + op.String())
	}
	insn := insn{op: op, line: fcomp.pos.Line}
	fcomp.block.insns = append(fcomp.block.insns, insn)
	fcomp.pos.Line = 0
}
func (fcomp *fcomp) emit1(op Opcode, arg uint32) {
	if op < OpcodeArgMin {
		panic("unwanted arg: " + op.String())
	}
	insn := insn{op: op, arg: arg, line: fcomp.pos.Line}
	fcomp.block.insns = append(fcomp.block.insns, insn)
	fcomp.pos.Line = 0
}
func (fcomp *fcomp) jump(b *block) {
	if b == fcomp.block {
		panic("self-jump")  
	}
	fcomp.block.jmp = b
	fcomp.block = nil
}
func (fcomp *fcomp) condjump(op Opcode, t, f *block) {
	if !(op == CJMP || op == ITERJMP) {
		panic("not a conditional jump: " + op.String())
	}
	fcomp.emit1(op, 0)  
	fcomp.block.cjmp = t
	fcomp.jump(f)
}
func (pcomp *pcomp) nameIndex(name string) uint32 {
	index, ok := pcomp.names[name]
	if !ok {
		index = uint32(len(pcomp.prog.Names))
		pcomp.names[name] = index
		pcomp.prog.Names = append(pcomp.prog.Names, name)
	}
	return index
}
func (pcomp *pcomp) constantIndex(v interface{}) uint32 {
	index, ok := pcomp.constants[v]
	if !ok {
		index = uint32(len(pcomp.prog.Constants))
		pcomp.constants[v] = index
		pcomp.prog.Constants = append(pcomp.prog.Constants, v)
	}
	return index
}