diff options
Diffstat (limited to 'usr/austin/eval/scope.go')
-rw-r--r-- | usr/austin/eval/scope.go | 161 |
1 files changed, 74 insertions, 87 deletions
diff --git a/usr/austin/eval/scope.go b/usr/austin/eval/scope.go index 8c9177b41..6d89d00d7 100644 --- a/usr/austin/eval/scope.go +++ b/usr/austin/eval/scope.go @@ -7,130 +7,117 @@ package eval import ( "eval"; "fmt"; + "log"; ) -func (s *Scope) Fork() *Scope { - return &Scope{ - outer: s, +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), - temps: make(map[int] *Variable) + 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 (s *Scope) DefineVar(name string, t Type) *Variable { - if _, ok := s.defs[name]; ok { +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, t Type) *Variable { + if _, ok := b.defs[name]; ok { return nil; } - v := &Variable{s.numVars, t}; - s.defs[name] = v; - s.numVars++; + v := b.DefineTemp(t); + if v != nil { + b.defs[name] = v; + } return v; } -func (s *Scope) DefineTemp(t Type) *Variable { - v := &Variable{s.numVars, t}; - s.temps[s.numVars] = v; - s.numVars++; +func (b *block) DefineTemp(t Type) *Variable { + if b.inner != nil { + log.Crash("Failed to exit child block before defining variable"); + } + index := b.offset+b.numVars; + v := &Variable{index, t}; + b.numVars++; + if index+1 > b.scope.maxVars { + b.scope.maxVars = index+1; + } return v; } -func (s *Scope) DefineConst(name string, t Type, v Value) *Constant { - if _, ok := s.defs[name]; ok { +func (b *block) DefineConst(name string, t Type, v Value) *Constant { + if _, ok := b.defs[name]; ok { return nil; } c := &Constant{t, v}; - s.defs[name] = c; + b.defs[name] = c; return c; } -func (s *Scope) DefineType(name string, t Type) Type { - if _, ok := s.defs[name]; ok { +func (b *block) DefineType(name string, 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. - nt := &NamedType{s, name, t.rep()}; - s.defs[name] = nt; + nt := &NamedType{name, t.rep()}; + b.defs[name] = nt; return nt; } -func (s *Scope) Lookup(name string) (Def, *Scope) { - for s != nil { - if d, ok := s.defs[name]; ok { - return d, s; +func (b *block) Lookup(name string) (level int, def Def) { + for b != nil { + if d, ok := b.defs[name]; ok { + return level, d; } - s = s.outer; + if b.outer != nil && b.scope != b.outer.scope { + level++; + } + b = b.outer; } - return nil, nil; + return 0, nil; } func (s *Scope) NewFrame(outer *Frame) *Frame { - if s.varTypes == nil { - // First creation of a frame from this scope. Compute - // and memoize the types of all variables. - ts := make([]Type, s.numVars); - for _, d := range s.defs { - if v, ok := d.(*Variable); ok { - // Record the representative type to - // avoid indirecting through named - // types every time we drop a frame. - ts[v.Index] = v.Type.rep(); - } - } - for _, v := range s.temps { - ts[v.Index] = v.Type.rep(); - } - s.varTypes = ts; - } - - // Create frame - // - // TODO(austin) This is probably rather expensive. All values - // require heap allocation and the Zero method typically - // requires some computation. - vars := make([]Value, s.numVars); - for i, t := range s.varTypes { - vars[i] = t.Zero(); - } - return &Frame{outer, s, vars}; + return outer.child(s.maxVars); } -func (f *Frame) Get(s *Scope, index int) Value { - for f.Scope != s { +func (f *Frame) Get(level int, index int) Value { + for ; level > 0; level-- { f = f.Outer; } return f.Vars[index]; } -func stringFrame(f *Frame) (string, string) { - res := ""; - indent := ""; - if f.Outer != nil { - res, indent = stringFrame(f.Outer); - } - - names := make([]string, f.Scope.numVars); - types := make([]Type, f.Scope.numVars); - for name, def := range f.Scope.defs { - def, ok := def.(*Variable); - if !ok { - continue; - } - names[def.Index] = name; - types[def.Index] = def.Type; - } - for _, def := range f.Scope.temps { - names[def.Index] = "(temp)"; - types[def.Index] = def.Type; - } - - for i, val := range f.Vars { - res += fmt.Sprintf("%s%-10s %-10s %s\n", indent, names[i], types[i], val); - } - return res, indent + " "; -} - -func (f *Frame) String() string { - res, _ := stringFrame(f); - return res; +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)}; } |