github.com/avicd/go-utilx@v0.1.0/evalx/eval.go (about)

     1  package evalx
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/avicd/go-utilx/conv"
     7  	"github.com/avicd/go-utilx/refx"
     8  	"github.com/avicd/go-utilx/tokx"
     9  	"go/ast"
    10  	"go/parser"
    11  	"go/token"
    12  	"reflect"
    13  	"strings"
    14  )
    15  
    16  func Eval(text string, cts ...Context) (any, error) {
    17  	text = strings.TrimSpace(text)
    18  	if text == "" {
    19  		return nil, errors.New("empty expression")
    20  	}
    21  	expr := tokx.DoubleQuota(text)
    22  	var stack *Stack
    23  	if len(cts) > 0 && cts[0] != nil {
    24  		stack = StackOf(cts[0])
    25  	} else {
    26  		stack = StackOf(NewScope())
    27  	}
    28  	var astExpr ast.Expr
    29  	if ae, ok := stack.Ctx.CacheOf(expr); ok {
    30  		astExpr = ae
    31  	} else {
    32  		var err error
    33  		astExpr, err = parser.ParseExpr(expr)
    34  		if err != nil {
    35  			return nil, err
    36  		} else {
    37  			stack.Ctx.Cache(expr, astExpr)
    38  		}
    39  	}
    40  	val := evalExpr(astExpr, stack)
    41  	if stack.Error != nil {
    42  		return nil, stack.Error
    43  	}
    44  	return val, nil
    45  }
    46  
    47  func evalExpr(buf ast.Expr, stack *Stack) any {
    48  	defer func() {
    49  		rc := recover()
    50  		if rc != nil {
    51  			stack.Error = refx.AsError(rc)
    52  		}
    53  	}()
    54  	switch expr := buf.(type) {
    55  	case *ast.SelectorExpr:
    56  		return evalSelector(expr, stack)
    57  	case *ast.Ident:
    58  		return evalIdent(expr, stack, false)
    59  	case *ast.BasicLit:
    60  		return evalBasicLit(expr)
    61  	case *ast.UnaryExpr:
    62  		return evalUnary(expr, stack)
    63  	case *ast.ParenExpr:
    64  		return evalExpr(expr.X, stack)
    65  	case *ast.IndexExpr:
    66  		return evalIndex(expr, stack)
    67  	case *ast.CallExpr:
    68  		return evalCall(expr, stack)
    69  	case *ast.BinaryExpr:
    70  		return evalBinary(expr, stack)
    71  	}
    72  	return nil
    73  }
    74  
    75  func evalSelector(expr *ast.SelectorExpr, stack *Stack) any {
    76  	target := stack.PopTarget()
    77  	var parent any
    78  	switch exprX := expr.X.(type) {
    79  	case *ast.SelectorExpr:
    80  		return evalSelector(exprX, stack)
    81  	case *ast.Ident:
    82  		parent = evalIdent(exprX, stack, true)
    83  	default:
    84  		parent = evalExpr(expr.X, stack)
    85  	}
    86  	if parent != nil {
    87  		if target == METHOD {
    88  			if method, ok := refx.MethodOf(parent, expr.Sel.Name); ok {
    89  				return method
    90  			}
    91  		}
    92  		if val, ok := refx.PropOf(parent, expr.Sel.Name); ok {
    93  			if target == METHOD {
    94  				if refx.IsFunc(val) {
    95  					return val
    96  				}
    97  			} else {
    98  				return val
    99  			}
   100  		}
   101  	}
   102  	return nil
   103  }
   104  
   105  func evalIndex(expr *ast.IndexExpr, stack *Stack) any {
   106  	target := stack.PopTarget()
   107  	obj := evalExpr(expr.X, stack)
   108  	index := evalExpr(expr.Index, stack)
   109  	if target == METHOD {
   110  		if method, ok := refx.MethodOf(obj, index); ok {
   111  			return method
   112  		}
   113  	}
   114  	if val, ok := refx.PropOf(obj, index); ok {
   115  		if target == METHOD {
   116  			if refx.IsFunc(val) {
   117  				return val
   118  			}
   119  		} else {
   120  			return val
   121  		}
   122  	}
   123  	return nil
   124  }
   125  
   126  func evalIdent(expr *ast.Ident, stack *Stack, sel bool) any {
   127  	target := stack.PopTarget()
   128  	if !sel {
   129  		switch expr.Name {
   130  		case "nil", "null":
   131  			return nil
   132  		case "true":
   133  			return true
   134  		case "false":
   135  			return false
   136  		}
   137  	}
   138  	if target == METHOD {
   139  		if method, ok := stack.Ctx.MethodOf(expr.Name); ok {
   140  			return method
   141  		}
   142  	}
   143  	if val, ok := stack.Ctx.ValueOf(expr.Name); ok {
   144  		if target == METHOD {
   145  			if refx.IsFunc(val) {
   146  				return val
   147  			}
   148  		} else {
   149  			return val
   150  		}
   151  	}
   152  	return nil
   153  }
   154  
   155  func evalUnary(expr *ast.UnaryExpr, stack *Stack) any {
   156  	ret := evalExpr(expr.X, stack)
   157  	switch expr.Op {
   158  	case token.NOT:
   159  		return !refx.AsBool(ret)
   160  	case token.SUB:
   161  		if refx.IsInteger(ret) {
   162  			return -refx.AsInt64(ret)
   163  		} else if refx.IsUInteger(ret) {
   164  			return -refx.AsUint64(ret)
   165  		} else if refx.IsFloat(ret) {
   166  			return -refx.AsFloat64(ret)
   167  		} else {
   168  			panic(fmt.Errorf("evalx: invalid operator '%s' on %v", expr.Op, expr.X))
   169  		}
   170  	case token.ADD:
   171  		if refx.IsNumber(ret) {
   172  			panic(fmt.Errorf("evalx: invalid operator '%s' on %v", expr.Op, expr.X))
   173  		}
   174  	}
   175  	return ret
   176  }
   177  
   178  func evalCall(expr *ast.CallExpr, stack *Stack) any {
   179  	stack.PushTarget(METHOD)
   180  	val := evalExpr(expr.Fun, stack)
   181  	if val == nil {
   182  		panic(fmt.Errorf("evalx: invalid function"))
   183  		return nil
   184  	}
   185  	method := refx.ValueOf(val)
   186  	var args []reflect.Value
   187  	for _, buf := range expr.Args {
   188  		argVal := evalExpr(buf, stack)
   189  		args = append(args, refx.ValueOf(argVal))
   190  	}
   191  	values := method.Call(args)
   192  	if len(values) > 0 {
   193  		return values[0].Interface()
   194  	}
   195  	return nil
   196  }
   197  
   198  func evalBinary(expr *ast.BinaryExpr, stack *Stack) any {
   199  	left := evalExpr(expr.X, stack)
   200  	if expr.Op == token.LOR && refx.AsBool(left) {
   201  		return true
   202  	}
   203  	var right any
   204  	if expr.Op == token.ADD {
   205  		if refx.IsString(left) {
   206  			right = evalString(expr.Y, stack)
   207  		} else {
   208  			right = evalExpr(expr.Y, stack)
   209  			if refx.IsString(right) {
   210  				left = evalString(expr.X, stack)
   211  			}
   212  		}
   213  	} else {
   214  		right = evalExpr(expr.Y, stack)
   215  	}
   216  	stack.X = left
   217  	stack.Y = right
   218  	var ret any
   219  	switch expr.Op {
   220  	case token.LOR:
   221  		ret = refx.AsBool(right)
   222  	case token.LAND:
   223  		ret = refx.AsBool(left) && refx.AsBool(right)
   224  	case token.EQL, token.NEQ, token.GTR, token.LSS, token.GEQ, token.LEQ:
   225  		ret = evalCmpBool(expr, left, right)
   226  	case token.ADD, token.SUB, token.MUL, token.QUO, token.REM:
   227  		ret = evalArithmetic(expr, stack)
   228  	case token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT:
   229  		ret = evalBitOpr(expr, stack)
   230  	}
   231  	return ret
   232  }
   233  
   234  func evalBasicLit(expr *ast.BasicLit) any {
   235  	switch expr.Kind {
   236  	case token.STRING:
   237  		return strings.Trim(expr.Value, "\"")
   238  	case token.CHAR:
   239  		return strings.Trim(expr.Value, "'")
   240  	case token.INT:
   241  		return conv.ParseInt(expr.Value)
   242  	case token.FLOAT:
   243  		return conv.ParseFloat(expr.Value)
   244  	default:
   245  		return nil
   246  	}
   247  }
   248  
   249  func evalString(input ast.Expr, stack *Stack) string {
   250  	if expr, ok := input.(*ast.BinaryExpr); ok {
   251  		if expr.Op == token.ADD {
   252  			left := evalString(expr.X, stack)
   253  			right := evalString(expr.Y, stack)
   254  			return left + right
   255  		}
   256  	}
   257  	return refx.AsString(evalExpr(input, stack))
   258  }
   259  
   260  func evalCmpBool(expr *ast.BinaryExpr, x, y any) bool {
   261  	var ret bool
   262  	cmp := refx.Cmp(x, y)
   263  	switch expr.Op {
   264  	case token.EQL:
   265  		ret = cmp == refx.CmpEq
   266  	case token.NEQ:
   267  		ret = cmp != refx.CmpEq
   268  	case token.GTR:
   269  		ret = cmp == refx.CmpGtr
   270  	case token.LSS:
   271  		ret = cmp == refx.CmpLss
   272  	case token.GEQ:
   273  		ret = cmp == refx.CmpGtr || cmp == refx.CmpEq
   274  	case token.LEQ:
   275  		ret = cmp == refx.CmpLss || cmp == refx.CmpEq
   276  	}
   277  	return ret
   278  }
   279  
   280  func evalArithmetic(expr *ast.BinaryExpr, stack *Stack) any {
   281  	var ret any
   282  	x := stack.X
   283  	y := stack.Y
   284  	if expr.Op == token.ADD && (refx.IsString(x) || refx.IsString(y)) {
   285  		return refx.AsString(x) + refx.AsString(y)
   286  	}
   287  	if refx.IsNumber(x) && refx.IsNumber(y) {
   288  		if refx.IsGeneralInt(x) && refx.IsGeneralInt(y) {
   289  			a := refx.AsInt64(x)
   290  			b := refx.AsInt64(y)
   291  			switch expr.Op {
   292  			case token.ADD:
   293  				ret = a + b
   294  			case token.SUB:
   295  				ret = a - b
   296  			case token.MUL:
   297  				ret = a * b
   298  			case token.QUO:
   299  				ret = a / b
   300  			case token.REM:
   301  				ret = a % b
   302  			}
   303  		} else {
   304  			a := refx.AsFloat64(x)
   305  			b := refx.AsFloat64(y)
   306  			switch expr.Op {
   307  			case token.ADD:
   308  				ret = a + b
   309  			case token.SUB:
   310  				ret = a - b
   311  			case token.MUL:
   312  				ret = a * b
   313  			case token.QUO:
   314  				ret = a / b
   315  			case token.REM:
   316  				panic(fmt.Errorf("evalx: invalid operator '%s' on float", expr.Op))
   317  			}
   318  		}
   319  	}
   320  	return ret
   321  }
   322  
   323  func evalBitOpr(expr *ast.BinaryExpr, stack *Stack) any {
   324  	var val uint64
   325  	x := stack.X
   326  	y := stack.Y
   327  	if refx.IsGeneralInt(x) && refx.IsGeneralInt(y) {
   328  		a := refx.AsUint64(x)
   329  		b := refx.AsUint64(y)
   330  		switch expr.Op {
   331  		case token.AND:
   332  			val = a & b
   333  		case token.OR:
   334  			val = a | b
   335  		case token.XOR:
   336  			val = a ^ b
   337  		case token.SHL:
   338  			val = a << b
   339  		case token.SHR:
   340  			val = a >> b
   341  		case token.AND_NOT:
   342  			val = a &^ b
   343  		}
   344  	} else {
   345  		panic(fmt.Errorf("evalx: bit operator work only on integer"))
   346  	}
   347  	return val
   348  }