mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
This PR introduces a new concept inside Expr's type checker - Nature. It's an abstraction other reflect.Type which carries additional information about possible type, methods and builtin function. I was able to find and fix a few bugs in type checker and optimizer. Implementation of nature concept has proven itself. As well nature will allow to bring more feature for type checker in future. Why Nature? I was looking for a name to replace Type. Type name already carries much information, as well can be confused with reflect.Type. Nature name should be understood as "nature of the node", same as "type of the node".
82 lines
1.8 KiB
Go
82 lines
1.8 KiB
Go
package optimizer
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
. "github.com/expr-lang/expr/ast"
|
|
"github.com/expr-lang/expr/file"
|
|
)
|
|
|
|
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
|
|
type constExpr struct {
|
|
applied bool
|
|
err error
|
|
fns map[string]reflect.Value
|
|
}
|
|
|
|
func (c *constExpr) Visit(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,
|
|
}
|
|
}
|
|
}()
|
|
|
|
if call, ok := (*node).(*CallNode); ok {
|
|
if name, ok := call.Callee.(*IdentifierNode); ok {
|
|
fn, ok := c.fns[name.Value]
|
|
if ok {
|
|
in := make([]reflect.Value, len(call.Arguments))
|
|
for i := 0; i < len(call.Arguments); i++ {
|
|
arg := call.Arguments[i]
|
|
var param any
|
|
|
|
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(¶m).Elem()
|
|
} else {
|
|
in[i] = reflect.ValueOf(param)
|
|
}
|
|
}
|
|
|
|
out := fn.Call(in)
|
|
value := out[0].Interface()
|
|
if len(out) == 2 && out[1].Type() == errorType && !out[1].IsNil() {
|
|
c.err = out[1].Interface().(error)
|
|
return
|
|
}
|
|
constNode := &ConstantNode{Value: value}
|
|
patchWithType(node, constNode)
|
|
c.applied = true
|
|
}
|
|
}
|
|
}
|
|
}
|