-
Notifications
You must be signed in to change notification settings - Fork 0
/
environment.go
119 lines (102 loc) · 2.47 KB
/
environment.go
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
package lox
import (
"fmt"
"strings"
)
type dynType int
const (
staticEnvironment dynType = iota
dynamicEnvironment
)
type Environment struct {
enclosing *Environment
locals []any
dynamics map[string]any
}
func NewEnvironment(t dynType) *Environment {
if t == dynamicEnvironment {
return &Environment{
dynamics: make(map[string]any),
}
}
return &Environment{}
}
func (env *Environment) isDynamic() bool {
return env.dynamics != nil
}
func (env *Environment) Debug() string {
var b strings.Builder
env.debug(&b)
return b.String()
}
func (env *Environment) debug(b *strings.Builder) {
if env.isDynamic() {
b.WriteString("- type: dynamic\n")
b.WriteString(" bindings:\n")
for name, value := range env.dynamics {
fmt.Fprintf(b, " %[1]s: {type: %[2]T, value: %[2]v}\n", name, value)
}
} else {
b.WriteString("- type: static\n")
b.WriteString(" bindings:\n")
for _, value := range env.locals {
fmt.Fprintf(b, " - {type: %[1]T, value: %[1]v}\n", value)
}
}
if env.enclosing != nil {
env.enclosing.debug(b)
}
}
// ----
func (env *Environment) Child(t dynType) *Environment {
if t == dynamicEnvironment && !env.isDynamic() {
panic("compiler error: dynamic environment can't be child of a static one")
}
child := NewEnvironment(t)
child.enclosing = env
return child
}
func (env *Environment) Define(name string, value any) {
if env.isDynamic() {
env.dynamics[name] = value
} else {
env.locals = append(env.locals, value)
}
}
func (env *Environment) Get(name Token) any {
for env != nil {
v, ok := env.dynamics[name.Lexeme]
if ok {
return v
}
env = env.enclosing
}
panic(runtimeError{name, fmt.Sprintf("undefined variable %q", name.Lexeme)})
}
func (env *Environment) Set(name Token, value any) {
for env != nil {
_, ok := env.dynamics[name.Lexeme]
if ok {
env.dynamics[name.Lexeme] = value
return
}
env = env.enclosing
}
panic(runtimeError{name, fmt.Sprintf("undefined variable %q", name.Lexeme)})
}
// ----
func (env *Environment) ancestor(distance int) *Environment {
for i := 0; i < distance; i++ {
env = env.enclosing
if env == nil || env.isDynamic() {
panic("compiler error: invalid or dynamic environment reached when looking for static ancestor")
}
}
return env
}
func (env *Environment) GetStatic(distance int, index int) any {
return env.ancestor(distance).locals[index]
}
func (env *Environment) SetStatic(distance int, index int, value any) {
env.ancestor(distance).locals[index] = value
}