mirror of
https://github.com/golang/go
synced 2024-11-26 17:56:55 -07:00
c7d27f88f8
successful. R=rsc APPROVED=rsc DELTA=43 (31 added, 0 deleted, 12 changed) OCL=34375 CL=34397
205 lines
4.7 KiB
Go
205 lines
4.7 KiB
Go
// 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)};
|
|
}
|