blob: 66305de25f034edb94b87a0944d2173b28c58596 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package eval
import (
"go/token"
"log"
)
/*
* Blocks and scopes
*/
// A definition can be a *Variable, *Constant, or Type.
type Def interface {
Pos() token.Pos
}
type Variable struct {
VarPos token.Pos
// Index of this variable in the Frame structure
Index int
// Static type of this variable
Type Type
// Value of this variable. This is only used by Scope.NewFrame;
// therefore, it is useful for global scopes but cannot be used
// in function scopes.
Init Value
}
func (v *Variable) Pos() token.Pos {
return v.VarPos
}
type Constant struct {
ConstPos token.Pos
Type Type
Value Value
}
func (c *Constant) Pos() token.Pos {
return c.ConstPos
}
// A block represents a definition block in which a name may not be
// defined more than once.
type block struct {
// The block enclosing this one, including blocks in other
// scopes.
outer *block
// The nested block currently being compiled, or nil.
inner *block
// The Scope containing this block.
scope *Scope
// The Variables, Constants, and Types defined in this block.
defs map[string]Def
// The index of the first variable defined in this block.
// This must be greater than the index of any variable defined
// in any parent of this block within the same Scope at the
// time this block is entered.
offset int
// The number of Variables defined in this block.
numVars int
// If global, do not allocate new vars and consts in
// the frame; assume that the refs will be compiled in
// using defs[name].Init.
global bool
}
// A Scope is the compile-time analogue of a Frame, which captures
// some subtree of blocks.
type Scope struct {
// The root block of this scope.
*block
// The maximum number of variables required at any point in
// this Scope. This determines the number of slots needed in
// Frame's created from this Scope at run-time.
maxVars int
}
func (b *block) enterChild() *block {
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Failed to exit child block before entering another child")
}
sub := &block{
outer: b,
scope: b.scope,
defs: make(map[string]Def),
offset: b.offset + b.numVars,
}
b.inner = sub
return sub
}
func (b *block) exit() {
if b.outer == nil {
log.Panic("Cannot exit top-level block")
}
if b.outer.scope == b.scope {
if b.outer.inner != b {
log.Panic("Already exited block")
}
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Exit of parent block without exit of child block")
}
}
b.outer.inner = nil
}
func (b *block) ChildScope() *Scope {
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Failed to exit child block before entering a child scope")
}
sub := b.enterChild()
sub.offset = 0
sub.scope = &Scope{sub, 0}
return sub.scope
}
func (b *block) DefineVar(name string, pos token.Pos, t Type) (*Variable, Def) {
if prev, ok := b.defs[name]; ok {
return nil, prev
}
v := b.defineSlot(t, false)
v.VarPos = pos
b.defs[name] = v
return v, nil
}
func (b *block) DefineTemp(t Type) *Variable { return b.defineSlot(t, true) }
func (b *block) defineSlot(t Type, temp bool) *Variable {
if b.inner != nil && b.inner.scope == b.scope {
log.Panic("Failed to exit child block before defining variable")
}
index := -1
if !b.global || temp {
index = b.offset + b.numVars
b.numVars++
if index >= b.scope.maxVars {
b.scope.maxVars = index + 1
}
}
v := &Variable{token.NoPos, index, t, nil}
return v
}
func (b *block) DefineConst(name string, pos token.Pos, t Type, v Value) (*Constant, Def) {
if prev, ok := b.defs[name]; ok {
return nil, prev
}
c := &Constant{pos, t, v}
b.defs[name] = c
return c, nil
}
func (b *block) DefineType(name string, pos token.Pos, t Type) Type {
if _, ok := b.defs[name]; ok {
return nil
}
nt := &NamedType{pos, name, nil, true, make(map[string]Method)}
if t != nil {
nt.Complete(t)
}
b.defs[name] = nt
return nt
}
func (b *block) Lookup(name string) (bl *block, level int, def Def) {
for b != nil {
if d, ok := b.defs[name]; ok {
return b, level, d
}
if b.outer != nil && b.scope != b.outer.scope {
level++
}
b = b.outer
}
return nil, 0, nil
}
func (s *Scope) NewFrame(outer *Frame) *Frame { return outer.child(s.maxVars) }
/*
* Frames
*/
type Frame struct {
Outer *Frame
Vars []Value
}
func (f *Frame) Get(level int, index int) Value {
for ; level > 0; level-- {
f = f.Outer
}
return f.Vars[index]
}
func (f *Frame) child(numVars int) *Frame {
// TODO(austin) This is probably rather expensive. All values
// require heap allocation and zeroing them when we execute a
// definition typically requires some computation.
return &Frame{f, make([]Value, numVars)}
}