github.com/MontFerret/ferret@v0.18.0/pkg/runtime/core/scope.go (about) 1 package core 2 3 import ( 4 "io" 5 ) 6 7 type ( 8 CloseFunc func() error 9 10 RootScope struct { 11 closed bool 12 disposables []io.Closer 13 } 14 15 Scope struct { 16 root *RootScope 17 parent *Scope 18 vars map[string]Value 19 } 20 ) 21 22 func NewRootScope() (*Scope, CloseFunc) { 23 root := &RootScope{ 24 closed: false, 25 disposables: make([]io.Closer, 0, 10), 26 } 27 28 return newScope(root, nil), root.Close 29 } 30 31 func (s *RootScope) AddDisposable(disposable io.Closer) { 32 if s.closed { 33 return 34 } 35 36 if disposable != nil { 37 s.disposables = append(s.disposables, disposable) 38 } 39 } 40 41 func (s *RootScope) Close() error { 42 if s.closed { 43 return Error(ErrInvalidOperation, "scope is already closed") 44 } 45 46 s.closed = true 47 48 var errors []error 49 50 // close all values implemented io.Close 51 for _, c := range s.disposables { 52 if err := c.Close(); err != nil { 53 if errors == nil { 54 errors = make([]error, 0, len(s.disposables)) 55 } 56 57 errors = append(errors, err) 58 } 59 } 60 61 if errors == nil { 62 return nil 63 } 64 65 return Errors(errors...) 66 } 67 68 func newScope(root *RootScope, parent *Scope) *Scope { 69 return &Scope{ 70 root: root, 71 parent: parent, 72 vars: make(map[string]Value), 73 } 74 } 75 76 func (s *Scope) SetVariable(name string, val Value) error { 77 if name != IgnorableVariable { 78 _, exists := s.vars[name] 79 80 // it already has been declared in the current scope 81 if exists { 82 return Errorf(ErrNotUnique, "variable is already declared: '%s'", name) 83 } 84 85 s.vars[name] = val 86 } 87 88 // we still want to make sure that nothing than needs to be closed leaks out 89 disposable, ok := val.(io.Closer) 90 91 if ok { 92 s.root.AddDisposable(disposable) 93 } 94 95 return nil 96 } 97 98 func (s *Scope) HasVariable(name string) bool { 99 _, exists := s.vars[name] 100 101 // does not exist in the current scope 102 // try to find in a parent scope 103 if !exists { 104 if s.parent != nil { 105 return s.parent.HasVariable(name) 106 } 107 } 108 109 return exists 110 } 111 112 func (s *Scope) GetVariable(name string) (Value, error) { 113 out, exists := s.vars[name] 114 115 // does not exist in the current scope 116 // try to find in the parent scope 117 if !exists { 118 if s.parent != nil { 119 return s.parent.GetVariable(name) 120 } 121 122 return nil, Errorf(ErrNotFound, "variable: '%s'", name) 123 } 124 125 return out, nil 126 } 127 128 func (s *Scope) MustGetVariable(name string) Value { 129 out, err := s.GetVariable(name) 130 131 if err != nil { 132 panic(err) 133 } 134 135 return out 136 } 137 138 func (s *Scope) UpdateVariable(name string, val Value) error { 139 _, exists := s.vars[name] 140 141 if !exists { 142 return Errorf(ErrNotFound, "variable: '%s'", name) 143 } 144 145 delete(s.vars, name) 146 147 return s.SetVariable(name, val) 148 } 149 150 func (s *Scope) Fork() *Scope { 151 child := newScope(s.root, s) 152 153 return child 154 }