summaryrefslogtreecommitdiff
path: root/usr/austin/eval/scope.go
blob: 3ddc55e48a46fc7d9b61e51d487467e4ecc77622 (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
// 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 (
	"eval";
	"fmt";
	"go/token";
	"log";
)

func (b *block) enterChild() *block {
	if b.inner != nil {
		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.inner != b {
		log.Crash("Already exited block");
	}
	if b.inner != nil {
		log.Crash("Exit of parent block without exit of child block");
	}
	b.outer.inner = nil;
}

func (b *block) ChildScope() *Scope {
	if b.inner != nil {
		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);
	v.Position = pos;
	b.defs[name] = v;
	return v, nil;
}

func (b *block) DefineSlot(t Type) *Variable {
	if b.inner != nil {
		log.Crash("Failed to exit child block before defining variable");
	}
	index := b.offset+b.numVars;
	v := &Variable{token.Position{}, index, t};
	b.numVars++;
	if index+1 > b.scope.maxVars {
		b.scope.maxVars = index+1;
	}
	return v;
}

func (b *block) DefineConst(name string, pos token.Position, t Type, v Value) *Constant {
	if _, ok := b.defs[name]; ok {
		return nil;
	}
	c := &Constant{pos, t, v};
	b.defs[name] = c;
	return c;
}

func (b *block) DefineType(name string, pos token.Position, t Type) Type {
	if _, ok := b.defs[name]; ok {
		return nil;
	}
	// We take the representative type of t because multiple
	// levels of naming are useless.
	if t != nil {
		t = t.lit();
	}
	nt := &NamedType{pos, name, t, false, make(map[string] Method)};
	b.defs[name] = nt;
	return nt;
}

func (b *block) Lookup(name string) (level int, def Def) {
	for b != nil {
		if d, ok := b.defs[name]; ok {
			return level, d;
		}
		if b.outer != nil && b.scope != b.outer.scope {
			level++;
		}
		b = b.outer;
	}
	return 0, nil;
}

func (s *Scope) NewFrame(outer *Frame) *Frame {
	return outer.child(s.maxVars);
}

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)};
}