summaryrefslogtreecommitdiff
path: root/src/pkg/exp/eval/scope.go
blob: 74ce32b594d45fb9efd48446fd9b3669eeeb50f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// 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)}
}