blob: 7e10293d5d89301cec895cfeb513380f11ec5942 [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 (
"fmt";
"go/token";
"log";
)
/*
* Blocks and scopes
*/
// A definition can be a *Variable, *Constant, or Type.
type Def interface {
Pos() token.Position;
}
type Variable struct {
token.Position;
// 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;
}
type Constant struct {
token.Position;
Type Type;
Value Value;
}
// 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.Crash("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.Crash("Cannot exit top-level block");
}
if b.outer.scope == b.scope {
if b.outer.inner != b {
log.Crash("Already exited block");
}
if b.inner != nil && b.inner.scope == b.scope {
log.Crash("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.Crash("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.Position, t Type) (*Variable, Def) {
if prev, ok := b.defs[name]; ok {
return nil, prev;
}
v := b.defineSlot(t, false);
v.Position = 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.Crash("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.Position{}, index, t, nil};
return v;
}
func (b *block) DefineConst(name string, pos token.Position, 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.Position, 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)};
}