Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Typecasting values automatically #703

Unanswered
sureshvytla asked this question in Q&A
Discussion options

There are cases where the numbers are sent as strings in the param map. Is there a way to typecast based on the expression?

Expression: 7 < "47" == true

You must be logged in to vote

Replies: 1 comment 3 replies

Comment options

Expr has int() builtin function.

Or you can use patch to modify expression before execution.

You must be logged in to vote
3 replies
Comment options

Hi @antonmedv Thanks for the inputs, I've tried the following simple approach to verify data type can be corrected, and it wasn't working.

var stringType = reflect.TypeOf("")
var numberType = reflect.TypeOf(0)
type NumberPatcher struct{}
func (NumberPatcher) Visit(node *ast.Node) {
	if n, ok := (*node).(*ast.BinaryNode); ok {
		l := n.Left
		r := n.Right
		fmt.Printf("Left %s (type: %s) \n", l, l.Type())
		fmt.Printf("Right %s (type: %s) \n", r, r.Type())
		fmt.Printf("Operator %s \n", n.Operator)
		switch n.Operator {
		case "<", ">", ">=", "<=", "==", "!=", "+", "-":
			if l.Type() == stringType && r.Type() == numberType {
				// patch the node to number type
				l.SetType(numberType)
				ast.Patch(&l, l)
			} else if l.Type() == numberType && r.Type() == stringType {
				// patch the node to number type
				r.SetType(numberType)
				ast.Patch(&r, r)
 // ast.Patch(&r, &ast.IntegerNode{Value: 47})
			}
		}
	}
}
func TestExpPatcher(t *testing.T) {
	env := map[string]interface{}{
		"a": 7,
		"b": "47",
	}
	code := `a < b == true`
	program, err := expr.Compile(code, expr.Env(env), expr.AllowUndefinedVariables(), expr.Patch(NumberPatcher{}))
	if err != nil {
		panic(err)
	}
	output, err := expr.Run(program, env)
	if err != nil {
		panic(err)
	}
	fmt.Println("Output:", output)
}

Even I tried with hardcoded node type, but no luck :(, Please correct me on the approach to handling different data types.

ast.Patch(&r, &ast.IntegerNode{Value: 47})

From the codebase, noticed type information is lost with the patch method, may I know what that means?

// Patch replaces the node with a new one.
// Location information is preserved.
// Type information is lost.
func Patch(node *Node, newNode Node) {
...
}

Also, I looked into the code, we compare data types in the binary node, is there a possibility to address handling different data types?

func (v *checker) BinaryNode(node *ast.BinaryNode) Nature {
Comment options

The issue you're encountering seems to stem from trying to modify the abstract syntax tree (AST) node types to force a type cast from string to int (or other numeric types) during the expression evaluation phase. Your approach with the NumberPatcher seems logical, but it’s important to ensure that the AST manipulation is done correctly and that the types are compatible after patching.

Here are some suggestions that might help you resolve this issue:

  1. Ensure Proper Type Conversion Logic: When patching the AST nodes, ensure that you are converting the string values to integers correctly. The ast.Patch() function replaces one node with another, but it doesn't automatically convert the data type. The patched node should be a correctly typed node (e.g., ast.IntegerNode).

  2. Type Information in Patching: As you noticed, ast.Patch() does lose type information. This can be problematic when the rest of the code relies on type information for further processing or evaluation. Ensure that after patching, the new node reflects the correct type. The loss of type information might be affecting the downstream type checks, causing unexpected behavior.

  3. Alternative Approach: Instead of directly manipulating the AST, consider pre-processing the expression or the environment map before passing it to expr.Compile(). For instance, you could convert all numeric strings in the environment map to their respective numeric types. This way, the expression you pass in will already have the correct types, and you won’t need to perform any AST manipulation.

    func convertEnvValues(env map[string]interface{}) {
     for k, v := range env {
     switch v := v.(type) {
     case string:
     if num, err := strconv.Atoi(v); err == nil {
     env[k] = num
     }
     }
     }
    }
  4. Using int() in Expression: Another straightforward approach is to enforce the type conversion directly in the expression string using the int() function provided by expr. For instance:

    code := `a < int(b) == true`

    This way, you sidestep the need for AST manipulation altogether.

  5. Debugging AST Changes: Add more debugging output to verify that the AST nodes are being modified as expected. Sometimes, the issue might not be in the typecasting itself but in the timing or the context in which the node is being patched.

Comment options

@antonmedv Thanks for the inputs, will try out the options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /