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  }