github.com/avicd/go-utilx@v0.1.0/evalx/context.go (about) 1 package evalx 2 3 import ( 4 "github.com/avicd/go-utilx/bufx" 5 "github.com/avicd/go-utilx/refx" 6 "go/ast" 7 ) 8 9 type Context interface { 10 ValueOf(ident string) (any, bool) 11 MethodOf(ident string) (any, bool) 12 CacheOf(text string) (ast.Expr, bool) 13 Cache(text string, expr ast.Expr) 14 } 15 16 type Scope struct { 17 binds map[string]any 18 backup map[string]any 19 callers map[string]any 20 vars []any 21 } 22 23 var top *Scope 24 var topCache bufx.Cache[string, ast.Expr] 25 26 func init() { 27 top = NewScope() 28 topCache = &bufx.LruCache[string, ast.Expr]{Size: 1000} 29 } 30 31 func SetCacheSize(size int) { 32 topCache = &bufx.LruCache[string, ast.Expr]{Size: size} 33 } 34 35 func TopScope() *Scope { 36 if top == nil { 37 top = NewScope() 38 } 39 return top 40 } 41 42 func NewScope(vars ...any) *Scope { 43 return &Scope{ 44 binds: map[string]any{}, 45 backup: map[string]any{}, 46 callers: map[string]any{}, 47 vars: vars, 48 } 49 } 50 51 func (it *Scope) ValueOf(ident string) (any, bool) { 52 if val, ok := refx.PropOfId(it.binds, ident); ok { 53 return val, true 54 } 55 for _, obj := range it.vars { 56 if val, ok := refx.PropOfId(obj, ident); ok { 57 return val, true 58 } 59 } 60 if !it.IsTop() { 61 return TopScope().ValueOf(ident) 62 } 63 return nil, false 64 } 65 66 func (it *Scope) MethodOf(ident string) (any, bool) { 67 for _, obj := range it.vars { 68 if method, ok := refx.MethodOfId(obj, ident); ok { 69 return method, true 70 } 71 } 72 if method, ok := it.callers[ident]; ok { 73 return method, true 74 } else if !it.IsTop() { 75 return TopScope().MethodOf(ident) 76 } 77 return nil, false 78 } 79 80 func (it *Scope) CacheOf(text string) (ast.Expr, bool) { 81 return topCache.Get(text) 82 } 83 84 func (it *Scope) Cache(text string, expr ast.Expr) { 85 topCache.Put(text, expr) 86 } 87 88 func (it *Scope) IsTop() bool { 89 return it == top 90 } 91 92 func (it *Scope) Bind(name string, value any) { 93 if refx.IsFunc(value) { 94 it.callers[name] = value 95 } else { 96 it.binds[name] = value 97 } 98 } 99 100 func (it *Scope) UnBind(name string) { 101 delete(it.binds, name) 102 } 103 104 func (it *Scope) Backup(name string) { 105 if val, ok := it.binds[name]; ok { 106 it.backup[name] = val 107 } 108 } 109 110 func (it *Scope) Restore(name string) { 111 if val, ok := it.backup[name]; ok { 112 it.binds[name] = val 113 delete(it.backup, name) 114 } 115 } 116 117 func (it *Scope) Merge(val any) *Scope { 118 var binds any 119 var vars []any 120 val = refx.ValueOf(val).Interface() 121 switch tmp := val.(type) { 122 case *Scope: 123 if tmp != nil { 124 binds = tmp.binds 125 vars = tmp.vars 126 } 127 case Scope: 128 binds = tmp.binds 129 vars = tmp.vars 130 default: 131 binds = val 132 } 133 if binds != nil { 134 if refx.IndirectType(binds) == refx.TypeOf(it.binds) { 135 refx.Merge(&it.binds, binds) 136 } else { 137 it.Link(val) 138 } 139 } 140 for _, item := range vars { 141 it.Link(item) 142 } 143 return it 144 } 145 146 func (it *Scope) Link(items ...any) *Scope { 147 if len(items) < 1 { 148 return it 149 } 150 var dest []any 151 dest = append(dest, items...) 152 dest = append(dest, it.vars...) 153 it.vars = dest 154 return it 155 } 156 157 func (it *Scope) UnLink(obj any) *Scope { 158 index := -1 159 for i, p := range it.vars { 160 if p == obj { 161 index = i 162 break 163 } 164 } 165 if index > -1 { 166 vars := it.vars[:index] 167 it.vars = append(vars, it.vars[index+1:]) 168 } 169 return it 170 } 171 172 func (it *Scope) Eval(text string) (any, error) { 173 return Eval(text, it) 174 }