| // 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.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)} | |
| } |