File: const_expr.go

package info (click to toggle)
golang-github-antonmedv-expr 1.8.9-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 4,524 kB
  • sloc: makefile: 6
file content (77 lines) | stat: -rw-r--r-- 1,653 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package optimizer

import (
	"fmt"
	. "github.com/antonmedv/expr/ast"
	"github.com/antonmedv/expr/file"
	"reflect"
	"strings"
)

type constExpr struct {
	applied bool
	err     error
	fns     map[string]reflect.Value
}

func (*constExpr) Enter(*Node) {}
func (c *constExpr) Exit(node *Node) {
	defer func() {
		if r := recover(); r != nil {
			msg := fmt.Sprintf("%v", r)
			// Make message more actual, it's a runtime error, but at compile step.
			msg = strings.Replace(msg, "runtime error:", "compile error:", 1)
			c.err = &file.Error{
				Location: (*node).Location(),
				Message:  msg,
			}
		}
	}()

	patch := func(newNode Node) {
		c.applied = true
		Patch(node, newNode)
	}

	switch n := (*node).(type) {
	case *FunctionNode:
		fn, ok := c.fns[n.Name]
		if ok {
			in := make([]reflect.Value, len(n.Arguments))
			for i := 0; i < len(n.Arguments); i++ {
				arg := n.Arguments[i]
				var param interface{}

				switch a := arg.(type) {
				case *NilNode:
					param = nil
				case *IntegerNode:
					param = a.Value
				case *FloatNode:
					param = a.Value
				case *BoolNode:
					param = a.Value
				case *StringNode:
					param = a.Value
				case *ConstantNode:
					param = a.Value

				default:
					return // Const expr optimization not applicable.
				}

				if param == nil && reflect.TypeOf(param) == nil {
					// In case of nil value and nil type use this hack,
					// otherwise reflect.Call will panic on zero value.
					in[i] = reflect.ValueOf(&param).Elem()
				} else {
					in[i] = reflect.ValueOf(param)
				}
			}

			out := fn.Call(in)
			constNode := &ConstantNode{Value: out[0].Interface()}
			patch(constNode)
		}
	}
}