Modify AST to represent function names as refs

These changes update the AST to represent function names as refs.
Previously, function names were represented as strings. Representing the
names as strings was fine, however, once functions and rules are
merged, it will be desirable to refer to functions using references.
This is a bit of preemptive refactoring to make that change easier.
Instead of having functions referred to with both strings and
references, all functions will be referred to with references.
This commit is contained in:
Torin Sandall
2017-10-03 14:15:07 -07:00
parent 315f239aef
commit ea2ea9b12b
26 changed files with 783 additions and 737 deletions
+81 -65
View File
@@ -4,7 +4,11 @@
package ast
import "github.com/open-policy-agent/opa/types"
import (
"strings"
"github.com/open-policy-agent/opa/types"
)
// Builtins is the registry of built-in functions supported by OPA.
// Call RegisterBuiltin to add a new built-in.
@@ -65,7 +69,7 @@ var DefaultBuiltins = [...]*Builtin{
// BuiltinMap provides a convenient mapping of built-in names to
// built-in definitions.
var BuiltinMap map[String]*Builtin
var BuiltinMap map[string]*Builtin
/**
* Unification
@@ -73,8 +77,8 @@ var BuiltinMap map[String]*Builtin
// Equality represents the "=" operator.
var Equality = &Builtin{
Name: String("eq"),
Infix: String("="),
Name: "eq",
Infix: "=",
Args: []types.Type{
types.A,
types.A,
@@ -88,8 +92,8 @@ var Equality = &Builtin{
// GreaterThan represents the ">" comparison operator.
var GreaterThan = &Builtin{
Name: String("gt"),
Infix: String(">"),
Name: "gt",
Infix: ">",
Args: []types.Type{
types.A,
types.A,
@@ -98,8 +102,8 @@ var GreaterThan = &Builtin{
// GreaterThanEq represents the ">=" comparison operator.
var GreaterThanEq = &Builtin{
Name: String("gte"),
Infix: String(">="),
Name: "gte",
Infix: ">=",
Args: []types.Type{
types.A,
types.A,
@@ -108,8 +112,8 @@ var GreaterThanEq = &Builtin{
// LessThan represents the "<" comparison operator.
var LessThan = &Builtin{
Name: String("lt"),
Infix: String("<"),
Name: "lt",
Infix: "<",
Args: []types.Type{
types.A,
types.A,
@@ -118,8 +122,8 @@ var LessThan = &Builtin{
// LessThanEq represents the "<=" comparison operator.
var LessThanEq = &Builtin{
Name: String("lte"),
Infix: String("<="),
Name: "lte",
Infix: "<=",
Args: []types.Type{
types.A,
types.A,
@@ -128,8 +132,8 @@ var LessThanEq = &Builtin{
// NotEqual represents the "!=" comparison operator.
var NotEqual = &Builtin{
Name: String("neq"),
Infix: String("!="),
Name: "neq",
Infix: "!=",
Args: []types.Type{
types.A,
types.A,
@@ -142,8 +146,8 @@ var NotEqual = &Builtin{
// Plus adds two numbers together.
var Plus = &Builtin{
Name: String("plus"),
Infix: String("+"),
Name: "plus",
Infix: "+",
Args: []types.Type{
types.N,
types.N,
@@ -155,8 +159,8 @@ var Plus = &Builtin{
// Minus subtracts the second number from the first number or computes the diff
// between two sets.
var Minus = &Builtin{
Name: String("minus"),
Infix: String("-"),
Name: "minus",
Infix: "-",
Args: []types.Type{
types.NewAny(types.N, types.NewSet(types.A)),
types.NewAny(types.N, types.NewSet(types.A)),
@@ -167,8 +171,8 @@ var Minus = &Builtin{
// Multiply multiplies two numbers together.
var Multiply = &Builtin{
Name: String("mul"),
Infix: String("*"),
Name: "mul",
Infix: "*",
Args: []types.Type{
types.N,
types.N,
@@ -179,8 +183,8 @@ var Multiply = &Builtin{
// Divide divides the first number by the second number.
var Divide = &Builtin{
Name: String("div"),
Infix: String("/"),
Name: "div",
Infix: "/",
Args: []types.Type{
types.N,
types.N,
@@ -191,7 +195,7 @@ var Divide = &Builtin{
// Round rounds the number up to the nearest integer.
var Round = &Builtin{
Name: String("round"),
Name: "round",
Args: []types.Type{
types.N,
types.N,
@@ -201,7 +205,7 @@ var Round = &Builtin{
// Abs returns the number without its sign.
var Abs = &Builtin{
Name: String("abs"),
Name: "abs",
Args: []types.Type{
types.N,
types.N,
@@ -217,8 +221,8 @@ var Abs = &Builtin{
// And performs an intersection operation on sets.
var And = &Builtin{
Name: String("and"),
Infix: String("&"),
Name: "and",
Infix: "&",
Args: []types.Type{
types.NewSet(types.A),
types.NewSet(types.A),
@@ -229,8 +233,8 @@ var And = &Builtin{
// Or performs a union operation on sets.
var Or = &Builtin{
Name: String("or"),
Infix: String("|"),
Name: "or",
Infix: "|",
Args: []types.Type{
types.NewSet(types.A),
types.NewSet(types.A),
@@ -245,7 +249,7 @@ var Or = &Builtin{
// Count takes a collection or string and counts the number of elements in it.
var Count = &Builtin{
Name: String("count"),
Name: "count",
Args: []types.Type{
types.NewAny(
types.NewSet(types.A),
@@ -260,7 +264,7 @@ var Count = &Builtin{
// Sum takes an array or set of numbers and sums them.
var Sum = &Builtin{
Name: String("sum"),
Name: "sum",
Args: []types.Type{
types.NewAny(
types.NewSet(types.N),
@@ -273,7 +277,7 @@ var Sum = &Builtin{
// Max returns the maximum value in a collection.
var Max = &Builtin{
Name: String("max"),
Name: "max",
Args: []types.Type{
types.NewAny(
types.NewSet(types.A),
@@ -286,7 +290,7 @@ var Max = &Builtin{
// Min returns the minimum value in a collection.
var Min = &Builtin{
Name: String("min"),
Name: "min",
Args: []types.Type{
types.NewAny(
types.NewSet(types.A),
@@ -305,7 +309,7 @@ var Min = &Builtin{
// Strings are converted to numbers using strconv.Atoi.
// Boolean false is converted to 0 and boolean true is converted to 1.
var ToNumber = &Builtin{
Name: String("to_number"),
Name: "to_number",
Args: []types.Type{
types.NewAny(
types.N,
@@ -325,7 +329,7 @@ var ToNumber = &Builtin{
// RegexMatch takes two strings and evaluates to true if the string in the second
// position matches the pattern in the first position.
var RegexMatch = &Builtin{
Name: String("re_match"),
Name: "re_match",
Args: []types.Type{
types.S,
types.S,
@@ -338,7 +342,7 @@ var RegexMatch = &Builtin{
// Concat joins an array of strings with an input string.
var Concat = &Builtin{
Name: String("concat"),
Name: "concat",
Args: []types.Type{
types.S,
types.NewAny(
@@ -352,7 +356,7 @@ var Concat = &Builtin{
// FormatInt returns the string representation of the number in the given base after converting it to an integer value.
var FormatInt = &Builtin{
Name: String("format_int"),
Name: "format_int",
Args: []types.Type{
types.N,
types.N,
@@ -363,7 +367,7 @@ var FormatInt = &Builtin{
// IndexOf returns the index of a substring contained inside a string
var IndexOf = &Builtin{
Name: String("indexof"),
Name: "indexof",
Args: []types.Type{
types.S,
types.S,
@@ -375,7 +379,7 @@ var IndexOf = &Builtin{
// Substring returns the portion of a string for a given start index and a length.
// If the length is less than zero, then substring returns the remainder of the string.
var Substring = &Builtin{
Name: String("substring"),
Name: "substring",
Args: []types.Type{
types.S,
types.N,
@@ -387,7 +391,7 @@ var Substring = &Builtin{
// Contains returns true if the search string is included in the base string
var Contains = &Builtin{
Name: String("contains"),
Name: "contains",
Args: []types.Type{
types.S,
types.S,
@@ -396,7 +400,7 @@ var Contains = &Builtin{
// StartsWith returns true if the search string begins with the base string
var StartsWith = &Builtin{
Name: String("startswith"),
Name: "startswith",
Args: []types.Type{
types.S,
types.S,
@@ -405,7 +409,7 @@ var StartsWith = &Builtin{
// EndsWith returns true if the search string begins with the base string
var EndsWith = &Builtin{
Name: String("endswith"),
Name: "endswith",
Args: []types.Type{
types.S,
types.S,
@@ -414,7 +418,7 @@ var EndsWith = &Builtin{
// Lower returns the input string but with all characters in lower-case
var Lower = &Builtin{
Name: String("lower"),
Name: "lower",
Args: []types.Type{
types.S,
types.S,
@@ -424,7 +428,7 @@ var Lower = &Builtin{
// Upper returns the input string but with all characters in upper-case
var Upper = &Builtin{
Name: String("upper"),
Name: "upper",
Args: []types.Type{
types.S,
types.S,
@@ -434,7 +438,7 @@ var Upper = &Builtin{
// Split returns an array containing elements of the input string split on a delimiter.
var Split = &Builtin{
Name: String("split"),
Name: "split",
Args: []types.Type{
types.S,
types.S,
@@ -446,7 +450,7 @@ var Split = &Builtin{
// Replace returns the given string with all instances of the second argument replaced
// by the third.
var Replace = &Builtin{
Name: String("replace"),
Name: "replace",
Args: []types.Type{
types.S,
types.S,
@@ -459,7 +463,7 @@ var Replace = &Builtin{
// Trim returns the given string will all leading or trailing instances of the second
// argument removed.
var Trim = &Builtin{
Name: String("trim"),
Name: "trim",
Args: []types.Type{
types.S,
types.S,
@@ -470,7 +474,7 @@ var Trim = &Builtin{
// Sprintf returns the given string, formatted.
var Sprintf = &Builtin{
Name: String("sprintf"),
Name: "sprintf",
Args: []types.Type{
types.S,
types.NewArray(nil, types.A),
@@ -485,7 +489,7 @@ var Sprintf = &Builtin{
// JSONMarshal serializes the input term.
var JSONMarshal = &Builtin{
Name: String("json.marshal"),
Name: "json.marshal",
Args: []types.Type{
types.A,
types.S,
@@ -495,7 +499,7 @@ var JSONMarshal = &Builtin{
// JSONUnmarshal deserializes the input string.
var JSONUnmarshal = &Builtin{
Name: String("json.unmarshal"),
Name: "json.unmarshal",
Args: []types.Type{
types.S,
types.A,
@@ -505,7 +509,7 @@ var JSONUnmarshal = &Builtin{
// Base64UrlEncode serializes the input string into base64url encoding.
var Base64UrlEncode = &Builtin{
Name: String("base64url.encode"),
Name: "base64url.encode",
Args: []types.Type{
types.S,
types.S,
@@ -515,7 +519,7 @@ var Base64UrlEncode = &Builtin{
// Base64UrlDecode deserializes the base64url encoded input string.
var Base64UrlDecode = &Builtin{
Name: String("base64url.decode"),
Name: "base64url.decode",
Args: []types.Type{
types.S,
types.S,
@@ -525,7 +529,7 @@ var Base64UrlDecode = &Builtin{
// YAMLMarshal serializes the input term.
var YAMLMarshal = &Builtin{
Name: String("yaml.marshal"),
Name: "yaml.marshal",
Args: []types.Type{
types.A,
types.S,
@@ -535,7 +539,7 @@ var YAMLMarshal = &Builtin{
// YAMLUnmarshal deserializes the input string.
var YAMLUnmarshal = &Builtin{
Name: String("yaml.unmarshal"),
Name: "yaml.unmarshal",
Args: []types.Type{
types.S,
types.A,
@@ -549,7 +553,7 @@ var YAMLUnmarshal = &Builtin{
// JWTDecode decodes a JSON Web Token and outputs it as an Object.
var JWTDecode = &Builtin{
Name: String("io.jwt.decode"),
Name: "io.jwt.decode",
Args: []types.Type{
types.S,
types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)),
@@ -565,7 +569,7 @@ var JWTDecode = &Builtin{
// NowNanos returns the current time since epoch in nanoseconds.
var NowNanos = &Builtin{
Name: String("time.now_ns"),
Name: "time.now_ns",
Args: []types.Type{
types.N,
},
@@ -574,7 +578,7 @@ var NowNanos = &Builtin{
// ParseNanos returns the time in nanoseconds parsed from the string in the given format.
var ParseNanos = &Builtin{
Name: String("time.parse_ns"),
Name: "time.parse_ns",
Args: []types.Type{
types.S,
types.S,
@@ -585,7 +589,7 @@ var ParseNanos = &Builtin{
// ParseRFC3339Nanos returns the time in nanoseconds parsed from the string in RFC3339 format.
var ParseRFC3339Nanos = &Builtin{
Name: String("time.parse_rfc3339_ns"),
Name: "time.parse_rfc3339_ns",
Args: []types.Type{
types.S,
types.N,
@@ -600,7 +604,7 @@ var ParseRFC3339Nanos = &Builtin{
// WalkBuiltin generates [path, value] tuples for all nested documents
// (recursively).
var WalkBuiltin = &Builtin{
Name: String("walk"),
Name: "walk",
Args: []types.Type{
types.A,
types.NewArray(
@@ -620,7 +624,7 @@ var WalkBuiltin = &Builtin{
// SetDiff has been replaced by the minus built-in.
var SetDiff = &Builtin{
Name: String("set_diff"),
Name: "set_diff",
Args: []types.Type{
types.NewSet(types.A),
types.NewSet(types.A),
@@ -632,23 +636,35 @@ var SetDiff = &Builtin{
// Builtin represents a built-in function supported by OPA. Every
// built-in function is uniquely identified by a name.
type Builtin struct {
Name String // Unique name of built-in function, e.g., name(term,term,...,term)
Infix String // Unique name of infix operator. Default should be unset.
Name string // Unique name of built-in function, e.g., <name>(arg1,arg2,...,argN)
Infix string // Unique name of infix operator. Default should be unset.
Args []types.Type // Built-in argument type declaration.
TargetPos []int // Argument positions that bind outputs. Indexing is zero-based.
}
// Expr creates a new expression for the built-in with the given terms.
func (b *Builtin) Expr(terms ...*Term) *Expr {
ts := []*Term{StringTerm(string(b.Name))}
for _, t := range terms {
ts = append(ts, t)
ts := make([]*Term, len(terms)+1)
ts[0] = NewTerm(b.Ref())
for i := range terms {
ts[i+1] = terms[i]
}
return &Expr{
Terms: ts,
}
}
// Ref returns a Ref that refers to the built-in function.
func (b *Builtin) Ref() Ref {
parts := strings.Split(b.Name, ".")
ref := make(Ref, len(parts))
ref[0] = VarTerm(parts[0])
for i := 1; i < len(parts); i++ {
ref[i] = StringTerm(parts[i])
}
return ref
}
// IsTargetPos returns true if a variable in the i-th position will be
// bound when the expression is evaluated.
func (b *Builtin) IsTargetPos(i int) bool {
@@ -661,7 +677,7 @@ func (b *Builtin) IsTargetPos(i int) bool {
}
func init() {
BuiltinMap = map[String]*Builtin{}
BuiltinMap = map[string]*Builtin{}
for _, b := range DefaultBuiltins {
RegisterBuiltin(b)
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
)
func TestIsTargetPos(t *testing.T) {
b := &Builtin{Name: String("dummy"), TargetPos: []int{1, 3}}
b := &Builtin{Name: "dummy", TargetPos: []int{1, 3}}
expected := []int{1, 3}
result := []int{}
for i := 0; i < 4; i++ {
+15 -8
View File
@@ -23,7 +23,7 @@ type exprChecker func(*TypeEnv, *Expr) *Error
// issues.
type typeChecker struct {
errs Errors
exprCheckers map[String]exprChecker
exprCheckers map[string]exprChecker
// When checking the types of functions, their inputs need to initially
// be assumed as types.Any. In order to fill the TypeEnv with more accurate
@@ -35,8 +35,8 @@ type typeChecker struct {
// newTypeChecker returns a new typeChecker object that has no errors.
func newTypeChecker() *typeChecker {
tc := &typeChecker{}
tc.exprCheckers = map[String]exprChecker{
Equality.Name: tc.checkExprEq,
tc.exprCheckers = map[string]exprChecker{
"eq": tc.checkExprEq,
}
return tc
}
@@ -163,7 +163,7 @@ func (tc *typeChecker) checkFunc(env *TypeEnv, fn *Func) {
if len(err) > prev {
return
}
name := fn.PathString()
name := fn.Path().String()
// Ensure that multiple definitions of this function have consistent argument
// lengths.
@@ -188,7 +188,7 @@ func (tc *typeChecker) checkFunc(env *TypeEnv, fn *Func) {
func (tc *typeChecker) checkLanguageBuiltins() *TypeEnv {
env := NewTypeEnv()
for _, bi := range Builtins {
env.PutFunc(bi.Name, bi.Args)
env.PutFunc(string(bi.Name), bi.Args)
}
return env
@@ -236,7 +236,7 @@ func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error {
return nil
}
checker := tc.exprCheckers[expr.Name()]
checker := tc.exprCheckers[expr.Name().String()]
if checker != nil {
return checker(env, expr)
}
@@ -245,7 +245,7 @@ func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error {
}
func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error {
name := expr.Name()
name := expr.Name().String()
expArgs := env.GetFunc(name)
if expArgs == nil {
return NewError(TypeErr, expr.Location, "undefined built-in function %v", name)
@@ -475,6 +475,13 @@ func (rc *refChecker) Visit(x interface{}) Visitor {
switch x := x.(type) {
case *ArrayComprehension, *ObjectComprehension, *SetComprehension:
return nil
case *Expr:
if terms, ok := x.Terms.([]*Term); ok {
for i := 1; i < len(terms); i++ {
Walk(rc, terms[i])
}
return nil
}
case Ref:
if err := rc.checkRef(rc.env, rc.env.tree, x, 0); err != nil {
rc.errs = append(rc.errs, err)
@@ -906,7 +913,7 @@ func newRefError(loc *Location, ref Ref) *Error {
return NewError(TypeErr, loc, "undefined ref: %v", ref)
}
func newArgError(loc *Location, builtinName String, msg string, have []types.Type, want []types.Type) *Error {
func newArgError(loc *Location, builtinName, msg string, have []types.Type, want []types.Type) *Error {
err := NewError(TypeErr, loc, "%v: %v", builtinName, msg)
err.Details = &ArgErrDetail{
Have: have,
+4 -6
View File
@@ -20,7 +20,7 @@ func TestCheckInference(t *testing.T) {
// fake_builtin_1([str1,str2])
RegisterBuiltin(&Builtin{
Name: String("fake_builtin_1"),
Name: "fake_builtin_1",
Args: []types.Type{
types.NewArray(
[]types.Type{types.S, types.S}, nil,
@@ -31,7 +31,7 @@ func TestCheckInference(t *testing.T) {
// fake_builtin_2({"a":str1,"b":str2})
RegisterBuiltin(&Builtin{
Name: String("fake_builtin_2"),
Name: "fake_builtin_2",
Args: []types.Type{
types.NewObject(
[]*types.StaticProperty{
@@ -45,7 +45,7 @@ func TestCheckInference(t *testing.T) {
// fake_builtin_3({str1,str2,...})
RegisterBuiltin(&Builtin{
Name: String("fake_builtin_3"),
Name: "fake_builtin_3",
Args: []types.Type{
types.NewSet(types.S),
},
@@ -549,7 +549,6 @@ func TestCheckMatchErrors(t *testing.T) {
{"object-dynamic", `{ obj2 = obj1 }`},
{"set", "{{1,2,3} = null}"},
}
for _, tc := range tests {
test.Subtest(t, tc.note, func(t *testing.T) {
body := MustParseBody(tc.query)
@@ -560,13 +559,12 @@ func TestCheckMatchErrors(t *testing.T) {
}
})
}
}
func TestCheckBuiltinErrors(t *testing.T) {
RegisterBuiltin(&Builtin{
Name: String("fake_builtin_2"),
Name: "fake_builtin_2",
Args: []types.Type{
types.NewAny(types.NewObject(
[]*types.StaticProperty{
+38 -24
View File
@@ -91,7 +91,7 @@ type Compiler struct {
// FunctionMap is a map containing the user defined functions of this
// compiler's modules.
FuncMap map[String][]*Func
FuncMap map[string][]*Func
// Graph represents the dependencies between rules and funcs (lets call
// them targets). An edge (u,v) is added to the graph if target "u"
@@ -194,7 +194,7 @@ func NewCompiler() *Compiler {
c := &Compiler{
Modules: map[string]*Module{},
TypeEnv: NewTypeEnv(),
FuncMap: map[String][]*Func{},
FuncMap: map[string][]*Func{},
generatedVars: map[*Module]VarSet{},
ruleIndices: util.NewHashMap(func(a, b util.T) bool {
r1, r2 := a.(Ref), b.(Ref)
@@ -405,7 +405,7 @@ func (c *Compiler) GetRules(ref Ref) (rules []*Rule) {
}
// GetFunc returns the function referred to by name.
func (c *Compiler) GetFunc(name String) []*Func {
func (c *Compiler) GetFunc(name string) []*Func {
if fn, ok := c.FuncMap[name]; ok {
return fn
}
@@ -413,14 +413,14 @@ func (c *Compiler) GetFunc(name String) []*Func {
}
// GetAllFuncs returns a map of functions that this compiler has discovered.
func (c *Compiler) GetAllFuncs() map[String][]*Func {
cpy := map[String][]*Func{}
func (c *Compiler) GetAllFuncs() map[string][]*Func {
cpy := map[string][]*Func{}
for _, fn := range c.FuncMap {
var fns []*Func
for _, f := range fn {
fns = append(fns, f.Copy())
}
cpy[fn[0].PathString()] = fns
cpy[fn[0].Path().String()] = fns
}
return cpy
}
@@ -618,7 +618,8 @@ func (c *Compiler) checkBodySafety(safe VarSet, m *Module, b Body, l *Location)
}
var safetyCheckVarVisitorParams = VarVisitorParams{
SkipClosures: true,
SkipRefCallHead: true,
SkipClosures: true,
}
// checkSafetyRuleHeads ensures that variables appearing in the head of a
@@ -768,7 +769,7 @@ func (c *Compiler) resolveAllRefs() {
})
WalkFuncs(mod, func(fn *Func) bool {
resolveRefsInFunc(globals, fn)
path := fn.PathString()
path := fn.Path().String()
c.FuncMap[path] = append(c.FuncMap[path], fn)
return false
@@ -779,14 +780,20 @@ func (c *Compiler) resolveAllRefs() {
}
for _, mod := range c.Modules {
visitor := NewGenericVisitor(func(x interface{}) bool {
// Walk terms in order to provide more detailed location
// information.
var visitor Visitor
visitor = NewGenericVisitor(func(x interface{}) bool {
switch x := x.(type) {
case *Expr:
if terms, ok := x.Terms.([]*Term); ok {
for i := 1; i < len(terms); i++ {
Walk(visitor, terms[i])
}
return true
}
case *Term:
switch v := x.Value.(type) {
case Ref:
if _, ok := c.FuncMap[String(v.String())]; ok {
if _, ok := c.FuncMap[v.String()]; ok {
c.err(&Error{
Code: CompileErr,
Message: x.Location.Format("%v refers to a known builtin but does not call it", string(x.Location.Text)),
@@ -1243,7 +1250,7 @@ type Graph struct {
// NewGraph returns a new Graph based on modules. The list function
// must return the rules or user functions referred to directly by the ref.
func NewGraph(modules map[string]*Module, list func(Ref) []*Rule, resolve func(String) []*Func) *Graph {
func NewGraph(modules map[string]*Module, list func(Ref) []*Rule, resolve func(string) []*Func) *Graph {
graph := &Graph{
adj: map[util.T]map[util.T]struct{}{},
@@ -1264,7 +1271,7 @@ func NewGraph(modules map[string]*Module, list func(Ref) []*Rule, resolve func(S
addFuncDeps := func(a util.T) func(expr *Expr) bool {
return func(expr *Expr) bool {
if expr.IsBuiltin() {
name := expr.Terms.([]*Term)[0].Value.(String)
name := expr.Terms.([]*Term)[0].String()
// Language builtins won't be resolved.
if b := resolve(name); b != nil {
@@ -1766,20 +1773,27 @@ func resolveRefsInExpr(globals map[Var]Ref, expr *Expr) *Expr {
case *Term:
cpy.Terms = resolveRefsInTerm(globals, ts)
case []*Term:
buf := []*Term{}
buf := make([]*Term, len(ts))
// Resolve user defined functions.
v := Var(ts[0].Value.(String))
if r, ok := globals[v]; ok {
tcpy := *ts[0]
tcpy.Value = String(r.String())
buf = append(buf, &tcpy)
ts = ts[1:]
// Resolve refs to functions inside the package. Refs outside the
// package must be fully qualified. FIXME(tsandall): this can go away
// once functions are merged with rules.
ref := ts[0].Value.(Ref)
if path, ok := globals[ref[0].Value.(Var)]; ok && len(ref) == 1 {
refCopy := path.Copy()
for i := range refCopy {
refCopy[i].SetLocation(ts[0].Location)
}
buf[0] = NewTerm(refCopy)
} else {
buf[0] = ts[0]
}
for _, t := range ts {
buf = append(buf, resolveRefsInTerm(globals, t))
// resolve remaining terms normally
for i := 1; i < len(ts); i++ {
buf[i] = resolveRefsInTerm(globals, ts[i])
}
cpy.Terms = buf
}
for _, w := range cpy.With {
+2
View File
@@ -427,6 +427,7 @@ func TestCompilerCheckSafetyBodyReordering(t *testing.T) {
contains(x, "oo")
`},
{"userfunc", `split(y, ".", z); a.b.funcs.fn("...foo.bar..", y)`, `a.b.funcs.fn("...foo.bar..", y); split(y, ".", z)`},
{"call-vars", `f.g[i](1); i = "foo"`, `i = "foo"; f.g[i](1)`},
}
for i, tc := range tests {
@@ -535,6 +536,7 @@ func TestCompilerCheckSafetyBodyErrors(t *testing.T) {
{"with-value-2", `p { x = data.a.b.d.t with input as x }`, `{x,}`},
{"else-kw", "p { false } else { count(x, 1) }", `{x,}`},
{"userfunc", "foo(x) = [y, z] { split(x, y, z) }", `{y,z}`},
{"call-vars", "p { f[i].g[j](1) }", `{i, j}`},
}
makeErrMsg := func(varName string) string {
+4 -4
View File
@@ -11,7 +11,7 @@ import (
// TypeEnv contains type info for static analysis such as type checking.
type TypeEnv struct {
funcs map[String][]types.Type
funcs map[string][]types.Type
tree *typeTreeNode
next *TypeEnv
}
@@ -19,7 +19,7 @@ type TypeEnv struct {
// NewTypeEnv returns an empty TypeEnv.
func NewTypeEnv() *TypeEnv {
return &TypeEnv{
funcs: map[String][]types.Type{},
funcs: map[string][]types.Type{},
tree: newTypeTree(),
}
}
@@ -27,7 +27,7 @@ func NewTypeEnv() *TypeEnv {
// GetFunc returns the type array corresponding to the arguments of the function
// referred to by name. GetFunc returns nil if there is no function matching that
// name.
func (env *TypeEnv) GetFunc(name String) []types.Type {
func (env *TypeEnv) GetFunc(name string) []types.Type {
tps, ok := env.funcs[name]
if !ok && env.next != nil {
return env.next.GetFunc(name)
@@ -37,7 +37,7 @@ func (env *TypeEnv) GetFunc(name String) []types.Type {
// PutFunc inserts the type information for the function referred to by name into
// this TypeEnv.
func (env *TypeEnv) PutFunc(name String, args []types.Type) {
func (env *TypeEnv) PutFunc(name string, args []types.Type) {
env.funcs[name] = args
}
+461 -480
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -415,7 +415,7 @@ func TestInfixArithExpr(t *testing.T) {
}
func TestMiscBuiltinExpr(t *testing.T) {
xyz := StringTerm("xyz")
xyz := RefTerm(VarTerm("xyz"))
assertParseOneExpr(t, "empty", "xyz()", NewBuiltinExpr(xyz))
assertParseOneExpr(t, "single", "xyz(abc)", NewBuiltinExpr(xyz, VarTerm("abc")))
assertParseOneExpr(t, "multiple", "xyz(abc, {\"one\": [1,2,3]})", NewBuiltinExpr(xyz, VarTerm("abc"), ObjectTerm(Item(StringTerm("one"), ArrayTerm(IntNumberTerm(1), IntNumberTerm(2), IntNumberTerm(3))))))
@@ -433,7 +433,7 @@ func TestNegatedExpr(t *testing.T) {
ref1 := RefTerm(VarTerm("x"), VarTerm("y"), StringTerm("z"), VarTerm("a"))
assertParseOneExprNegated(t, "membership", "not x[y].z[a] = \"b\"", Equality.Expr(ref1, StringTerm("b")))
assertParseOneExprNegated(t, "misc. builtin", "not sorted(x[y].z[a])", NewBuiltinExpr(StringTerm("sorted"), ref1))
assertParseOneExprNegated(t, "misc. builtin", "not sorted(x[y].z[a])", NewBuiltinExpr(RefTerm(VarTerm("sorted")), ref1))
}
func TestExprWith(t *testing.T) {
@@ -578,21 +578,21 @@ func TestUserFunctions(t *testing.T) {
assertParseFunc(t, "term input", `f([x, y]) = z { split(x, y, z) }`, &Func{
Head: NewFuncHead(Var("f"), VarTerm("z"), ArrayTerm(VarTerm("x"), VarTerm("y"))),
Body: NewBody(
&Expr{Terms: []*Term{StringTerm("split"), VarTerm("x"), VarTerm("y"), VarTerm("z")}},
Split.Expr(VarTerm("x"), VarTerm("y"), VarTerm("z")),
),
})
assertParseFunc(t, "term output", `f() = [x, y] { split("foo.bar", x, y) }`, &Func{
Head: NewFuncHead(Var("f"), ArrayTerm(VarTerm("x"), VarTerm("y"))),
Body: NewBody(
&Expr{Terms: []*Term{StringTerm("split"), StringTerm("foo.bar"), VarTerm("x"), VarTerm("y")}},
Split.Expr(StringTerm("foo.bar"), VarTerm("x"), VarTerm("y")),
),
})
assertParseFunc(t, "comprehension", `f(x) = y { count([1 | x[_]], y) }`, &Func{
Head: NewFuncHead(Var("f"), VarTerm("y"), VarTerm("x")),
Body: NewBody(
&Expr{Terms: []*Term{StringTerm("count"), MustParseTerm("[1 | x[_]]"), VarTerm("y")}},
Count.Expr(MustParseTerm("[1 | x[_]]"), VarTerm("y")),
),
})
@@ -1225,7 +1225,7 @@ func TestNamespacedBuiltins(t *testing.T) {
expected *Term
wantErr bool
}{
{`foo.bar.baz(1, 2)`, StringTerm("foo.bar.baz"), false},
{`foo.bar.baz(1, 2)`, MustParseTerm("foo.bar.baz"), false},
{`foo.(1,2)`, nil, true},
{`foo.#.bar(1,2)`, nil, true},
{`foo(1,2,3).bar`, nil, true},
+10 -18
View File
@@ -541,11 +541,6 @@ func (f *Func) Path() Ref {
return global
}
// PathString returns a String type representing the full path of this Func.
func (f *Func) PathString() String {
return String(f.Path().String())
}
func (f *Func) String() string {
return f.Head.String() + " { " + f.Body.String() + " }"
}
@@ -999,10 +994,7 @@ func (expr *Expr) IsEquality() bool {
if !ok {
return false
}
if len(terms) != 3 {
return false
}
return terms[0].Value.Compare(Equality.Name) == 0
return terms[0].Value.Compare(Equality.Ref()) == 0
}
// IsBuiltin returns true if this expression refers to a function.
@@ -1011,14 +1003,14 @@ func (expr *Expr) IsBuiltin() bool {
return ok
}
// Name returns the name of the user function or built-in this expression refers to. If
// this expression is not a function call, returns the empty string.
func (expr *Expr) Name() String {
// Name returns the name of the user function or built-in this expression
// refers to. If this expression is not a function call, returns nil.
func (expr *Expr) Name() Ref {
terms, ok := expr.Terms.([]*Term)
if !ok || len(terms) == 0 {
return ""
return nil
}
return terms[0].Value.(String)
return terms[0].Value.(Ref)
}
// Operand returns the term at the zero-based pos. If the expr does not include
@@ -1075,9 +1067,9 @@ func (expr *Expr) OutputVars(safe VarSet) VarSet {
case *Term:
return expr.outputVarsRefs(safe)
case []*Term:
name := terms[0].Value.(String)
name := terms[0].String()
if b := BuiltinMap[name]; b != nil {
if b.Name.Equal(Equality.Name) {
if b.Name == Equality.Name {
return expr.outputVarsEquality(safe)
}
return expr.outputVarsBuiltins(b, safe)
@@ -1108,7 +1100,7 @@ func (expr *Expr) String() string {
}
switch t := expr.Terms.(type) {
case []*Term:
name := t[0].Value.(String)
name := t[0].String()
bi := BuiltinMap[name]
var s string
if bi != nil && len(bi.Infix) > 0 {
@@ -1127,7 +1119,7 @@ func (expr *Expr) String() string {
for _, v := range t[1:] {
args = append(args, v.String())
}
name := string(t[0].Value.(String))
name := string(t[0].String())
s = fmt.Sprintf("%s(%s)", name, strings.Join(args, ", "))
}
+19 -30
View File
@@ -219,7 +219,7 @@ func TestBodyIsGround(t *testing.T) {
func TestExprOutputVars(t *testing.T) {
RegisterBuiltin(&Builtin{
Name: String("test_out_array"),
Name: "test_out_array",
Args: []types.Type{
types.NewArray(nil, types.N),
},
@@ -227,7 +227,7 @@ func TestExprOutputVars(t *testing.T) {
})
RegisterBuiltin(&Builtin{
Name: String("test_out_set"),
Name: "test_out_set",
Args: []types.Type{
types.NewArray(nil, types.N),
},
@@ -235,7 +235,7 @@ func TestExprOutputVars(t *testing.T) {
})
RegisterBuiltin(&Builtin{
Name: String("foo"),
Name: "foo",
Args: []types.Type{
types.A,
types.A,
@@ -292,18 +292,13 @@ func TestExprString(t *testing.T) {
Negated: true,
Terms: RefTerm(VarTerm("q"), StringTerm("r"), VarTerm("x")),
}
expr3 := &Expr{
Terms: []*Term{StringTerm("="), StringTerm("a"), FloatNumberTerm(17.1)},
}
expr4 := &Expr{
Terms: []*Term{
StringTerm("!="),
ObjectTerm(Item(VarTerm("foo"), ArrayTerm(
IntNumberTerm(1), RefTerm(VarTerm("a"), StringTerm("b")),
))),
BooleanTerm(false),
},
}
expr3 := Equality.Expr(StringTerm("a"), FloatNumberTerm(17.1))
expr4 := NotEqual.Expr(
ObjectTerm(Item(VarTerm("foo"), ArrayTerm(
IntNumberTerm(1), RefTerm(VarTerm("a"), StringTerm("b")),
))),
BooleanTerm(false),
)
expr5 := &Expr{
Terms: BooleanTerm(true),
With: []*With{
@@ -317,21 +312,15 @@ func TestExprString(t *testing.T) {
},
},
}
expr6 := &Expr{
Terms: []*Term{
StringTerm("+"),
IntNumberTerm(1),
IntNumberTerm(2),
IntNumberTerm(3),
},
}
expr7 := &Expr{
Terms: []*Term{
StringTerm("count"),
StringTerm("foo"),
VarTerm("x"),
},
}
expr6 := Plus.Expr(
IntNumberTerm(1),
IntNumberTerm(2),
IntNumberTerm(3),
)
expr7 := Count.Expr(
StringTerm("foo"),
VarTerm("x"),
)
assertExprString(t, expr1, "q.r[x]")
assertExprString(t, expr2, "not q.r[x]")
assertExprString(t, expr3, "\"a\" = 17.1")
+15 -17
View File
@@ -457,8 +457,8 @@ ArithInfixOp <- val:("+" / "-" / "*" / "/" / "&" / "|" / "-") {
op = string(b.Name)
}
}
operator := StringTerm(op)
operator.Location = currentLocation(c)
loc := currentLocation(c)
operator := RefTerm(VarTerm(op).SetLocation(loc)).SetLocation(loc)
return operator, nil
}
@@ -473,14 +473,14 @@ InfixOp <- val:("=" / "!=" / "<=" / ">=" / "<" / ">") {
op = string(b.Name)
}
}
operator := StringTerm(op)
operator.Location = currentLocation(c)
loc := currentLocation(c)
operator := RefTerm(VarTerm(op).SetLocation(loc)).SetLocation(loc)
return operator, nil
}
PrefixExpr <- SetEmpty / Builtin
PrefixExpr <- SetEmpty / Call
Builtin <- name:BuiltinName "(" _ head:Term? tail:( _ "," _ Term )* _ ")" {
Call <- name:Operator "(" _ head:Term? tail:( _ "," _ Term )* _ ")" {
buf := []*Term{name.(*Term)}
if head == nil {
return buf, nil
@@ -496,18 +496,16 @@ Builtin <- name:BuiltinName "(" _ head:Term? tail:( _ "," _ Term )* _ ")" {
return buf, nil
}
BuiltinName <- head:Var tail:( "." Var )* {
tailSlice := tail.([]interface{})
buf := make([]string, 1+len(tailSlice))
buf[0] = string(head.(*Term).Value.(Var))
for i := range tailSlice {
elem := tailSlice[i]
part := elem.([]interface{})[1].(*Term).Value.(Var)
buf[i+1] = string(part)
Operator <- val:(Ref / Var) {
term := val.(*Term)
switch term.Value.(type) {
case Ref:
return val, nil
case Var:
return RefTerm(term).SetLocation(currentLocation(c)), nil
default:
panic("unreachable")
}
name := StringTerm(strings.Join(buf, "."))
name.Location = currentLocation(c)
return name, nil
}
Term <- val:( Comprehension / Composite / Scalar / Ref / Var ) {
+2 -2
View File
@@ -53,8 +53,8 @@ func TestUnify(t *testing.T) {
}
terms := expr.Terms.([]*Term)
if terms[0].Value.Compare(Equality.Name) != 0 {
panic(terms)
if !expr.IsEquality() {
panic(expr)
}
a, b := terms[1], terms[2]
+23 -6
View File
@@ -243,12 +243,13 @@ type VarVisitor struct {
// VarVisitorParams contains settings for a VarVisitor.
type VarVisitorParams struct {
SkipRefHead bool
SkipObjectKeys bool
SkipClosures bool
SkipWithTarget bool
SkipSets bool
SkipFuncVars bool
SkipRefHead bool
SkipRefCallHead bool
SkipObjectKeys bool
SkipClosures bool
SkipWithTarget bool
SkipSets bool
SkipFuncVars bool
}
// NewVarVisitor returns a new VarVisitor object.
@@ -304,6 +305,22 @@ func (vis *VarVisitor) Visit(v interface{}) Visitor {
return nil
}
}
if vis.params.SkipRefCallHead {
if expr, ok := v.(*Expr); ok {
if terms, ok := expr.Terms.([]*Term); ok {
for _, t := range terms[0].Value.(Ref)[1:] {
Walk(vis, t)
}
for i := 1; i < len(terms); i++ {
Walk(vis, terms[i])
}
for _, w := range expr.With {
Walk(vis, w)
}
return nil
}
}
}
if vis.params.SkipFuncVars {
if f, ok := v.(*Func); ok {
Walk(vis, f.Body)
+59 -27
View File
@@ -23,11 +23,19 @@ func TestVisitor(t *testing.T) {
import input.x.y as z
t[x] = y { p[x] = {"foo": [y, 2, {"bar": 3}]}; not q[x]; y = [[x, z] | x = "x"; z = "z"]; z = {"foo": [x, z] | x = "x"; z = "z"}; s = {1 | a[i] = "foo"}; count({1, 2, 3}, n) with input.foo.bar as x }
t[x] = y {
p[x] = {"foo": [y, 2, {"bar": 3}]}
not q[x]
y = [[x, z] | x = "x"; z = "z"]
z = {"foo": [x, z] | x = "x"; z = "z"}
s = {1 | a[i] = "foo"}
count({1, 2, 3}, n) with input.foo.bar as x
}
p { false } else { false } else { true }
fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
fn([x, y]) = z { json.unmarshal(x, z); z > y }
`)
vis := &testVis{}
Walk(vis, rule)
@@ -62,7 +70,9 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
body
expr1
term
=
ref
term
=
term
ref1
term
@@ -94,7 +104,9 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
x
expr3
term
=
ref
term
=
term
y
term
@@ -108,21 +120,27 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
body
expr4
term
=
ref
term
=
term
x
term
"x"
expr5
term
=
ref
term
=
term
z
term
"z"
expr4
term
=
ref
term
=
term
z
term
@@ -139,21 +157,27 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
body
expr1
term
=
ref
term
=
term
x
term
"x"
expr2
term
=
ref
term
=
term
z
term
"z"
expr5
term
=
ref
term
=
term
s
term
@@ -163,7 +187,9 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
body
expr1
term
=
ref
term
=
term
ref
term
@@ -175,7 +201,9 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
"foo"
expr6
term
count
ref
term
count
term
set
term
@@ -241,23 +269,27 @@ fn([x, y]) = z { z = "bar"; trim(x, y, z) }`)
body
expr1
term
=
term
z
term
"bar"
expr2
term
trim
ref
term
json
term
unmarshal
term
x
term
y
z
expr2
term
ref
term
>
term
z
term
y
*/
if len(vis.elems) != 216 {
t.Errorf("Expected exactly 216 elements in AST but got %d: %v", len(vis.elems), vis.elems)
if len(vis.elems) != 240 {
t.Errorf("Expected exactly 240 elements in AST but got %d: %v", len(vis.elems), vis.elems)
}
}
@@ -268,7 +300,7 @@ func TestWalkVars(t *testing.T) {
found.Add(v)
return false
})
expected := NewVarSet(Var("x"), Var("data"), Var("y"), Var("z"), Var("q"))
expected := NewVarSet(Var("x"), Var("data"), Var("y"), Var("z"), Var("q"), Var("eq"))
if !expected.Equal(found) {
t.Fatalf("Expected %v but got: %v", expected, found)
}
@@ -281,11 +313,11 @@ func TestVarVisitor(t *testing.T) {
params VarVisitorParams
expected string
}{
{"data.foo[x] = bar.baz[y]", VarVisitorParams{SkipRefHead: true}, "[x, y]"},
{"{x: y}", VarVisitorParams{SkipObjectKeys: true}, "[y]"},
{`foo = [x | data.a[i] = x]`, VarVisitorParams{SkipClosures: true}, "[foo]"},
{`x = 1; y = 2; z = x + y; count([x, y, z], z)`, VarVisitorParams{}, "[x, y, z]"},
{"foo with input.bar.baz as qux[corge]", VarVisitorParams{SkipWithTarget: true}, "[foo, qux, corge]"},
{"data.foo[x] = bar.baz[y]", VarVisitorParams{SkipRefHead: true}, "[x, y]"},
{`foo = [x | data.a[i] = x]`, VarVisitorParams{SkipClosures: true}, "[foo, eq]"},
{`x = 1; y = 2; z = x + y; count([x, y, z], z)`, VarVisitorParams{}, "[x, y, z, eq, plus, count]"},
}
for _, tc := range tests {
+2 -2
View File
@@ -368,7 +368,7 @@ func (w *writer) writeExpr(expr *ast.Expr, comments []*ast.Comment) []*ast.Comme
}
func (w *writer) writeFunctionCall(t []*ast.Term, comments []*ast.Comment) []*ast.Comment {
name := t[0].Value.(ast.String)
name := t[0].Value.String()
bi := ast.BuiltinMap[name]
if bi != nil && len(bi.Infix) > 0 {
switch len(bi.Args) {
@@ -383,7 +383,7 @@ func (w *writer) writeFunctionCall(t []*ast.Term, comments []*ast.Comment) []*as
}
}
w.write(string(t[0].Value.(ast.String)) + "(")
w.write(string(t[0].String()) + "(")
for _, v := range t[1 : len(t)-1] {
comments = w.writeTerm(v, comments)
w.write(", ")
+1 -1
View File
@@ -54,7 +54,7 @@ func TestRegoCaptureTermsRewrite(t *testing.T) {
func TestRegoCancellation(t *testing.T) {
ast.RegisterBuiltin(&ast.Builtin{
Name: ast.String("test.sleep"),
Name: "test.sleep",
Args: []types.Type{
types.S,
},
+1 -1
View File
@@ -491,7 +491,7 @@ func (r *REPL) unsetFunc(ctx context.Context, v ast.Value) error {
mod := r.modules[r.currentModuleID]
funcs := []*ast.Func{}
for _, f := range mod.Funcs {
if !f.PathString().Equal(ast.String(ref.String())) {
if f.Path().String() != ref.String() {
funcs = append(funcs, f)
}
}
+2 -2
View File
@@ -386,7 +386,7 @@ func TestUnset(t *testing.T) {
repl.OneShot(ctx, "unset repl.p")
err = repl.OneShot(ctx, "repl.p(5, y)")
if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined built-in function "repl.p"` {
if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined built-in function repl.p` {
t.Fatalf("Expected eval error (undefined built-in) but got err: '%v'", err)
}
@@ -396,7 +396,7 @@ func TestUnset(t *testing.T) {
repl.OneShot(ctx, "unset repl.p")
err = repl.OneShot(ctx, "repl.p(1, 2, y)")
if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined built-in function "repl.p"` {
if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined built-in function repl.p` {
t.Fatalf("Expected eval error (undefined built-in) but got err: '%v'", err)
}
+2 -2
View File
@@ -1341,8 +1341,8 @@ func TestQueryWatchMigrateInvalidate(t *testing.T) {
"HTTP/1.1 200 OK\nContent-Type: application/json\nTransfer-Encoding: chunked\n\n7c",
`{"result":[{"expressions":[{"value":true,"text":"a=data.z.r+data.x","location":{"row":1,"col":1}}],"bindings":{"a":-200}}]}
`,
`d7`,
`{"result":null,"error":{"code":"evaluation_error","message":"watch invalidated: 1 error occurred: 1:1: rego_type_error: \"plus\": invalid argument(s)\n\thave: (string, any, ???)\n\twant: (number, number, number)"}}
`d3`,
`{"result":null,"error":{"code":"evaluation_error","message":"watch invalidated: 1 error occurred: 1:1: rego_type_error: plus: invalid argument(s)\n\thave: (string, any, ???)\n\twant: (number, number, number)"}}
`,
`0`,
``,
+1 -1
View File
@@ -71,7 +71,7 @@ func TestRun(t *testing.T) {
func TestRunnerCancel(t *testing.T) {
ast.RegisterBuiltin(&ast.Builtin{
Name: ast.String("test.sleep"),
Name: "test.sleep",
Args: []types.Type{
types.S,
},
+17 -17
View File
@@ -91,43 +91,43 @@ type (
)
// RegisterBuiltinFunc adds a new built-in function to the evaluation engine.
func RegisterBuiltinFunc(name ast.String, fun BuiltinFunc) {
func RegisterBuiltinFunc(name string, fun BuiltinFunc) {
builtinFunctions[name] = fun
}
// RegisterFunctionalBuiltinVoid1 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltinVoid1(name ast.String, fun FunctionalBuiltinVoid1) {
func RegisterFunctionalBuiltinVoid1(name string, fun FunctionalBuiltinVoid1) {
builtinFunctions[name] = functionalWrapperVoid1(name, fun)
}
// RegisterFunctionalBuiltinVoid2 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltinVoid2(name ast.String, fun FunctionalBuiltinVoid2) {
func RegisterFunctionalBuiltinVoid2(name string, fun FunctionalBuiltinVoid2) {
builtinFunctions[name] = functionalWrapperVoid2(name, fun)
}
// RegisterFunctionalBuiltin1 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin1(name ast.String, fun FunctionalBuiltin1) {
func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1) {
builtinFunctions[name] = functionalWrapper1(name, fun)
}
// RegisterFunctionalBuiltin2 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin2(name ast.String, fun FunctionalBuiltin2) {
func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2) {
builtinFunctions[name] = functionalWrapper2(name, fun)
}
// RegisterFunctionalBuiltin3 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin3(name ast.String, fun FunctionalBuiltin3) {
func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3) {
builtinFunctions[name] = functionalWrapper3(name, fun)
}
// RegisterFunctionalBuiltin1Out3 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin1Out3(name ast.String, fun FunctionalBuiltin1Out3) {
func RegisterFunctionalBuiltin1Out3(name string, fun FunctionalBuiltin1Out3) {
builtinFunctions[name] = functionalWrapper1Out3(name, fun)
}
@@ -150,9 +150,9 @@ func (BuiltinEmpty) Error() string {
return "<empty>"
}
var builtinFunctions = map[ast.String]BuiltinFunc{}
var builtinFunctions = map[string]BuiltinFunc{}
func functionalWrapperVoid1(name ast.String, fn FunctionalBuiltinVoid1) BuiltinFunc {
func functionalWrapperVoid1(name string, fn FunctionalBuiltinVoid1) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, 1)
@@ -167,7 +167,7 @@ func functionalWrapperVoid1(name ast.String, fn FunctionalBuiltinVoid1) BuiltinF
}
}
func functionalWrapperVoid2(name ast.String, fn FunctionalBuiltinVoid2) BuiltinFunc {
func functionalWrapperVoid2(name string, fn FunctionalBuiltinVoid2) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, 2)
@@ -182,7 +182,7 @@ func functionalWrapperVoid2(name ast.String, fn FunctionalBuiltinVoid2) BuiltinF
}
}
func functionalWrapper1(name ast.String, fn FunctionalBuiltin1) BuiltinFunc {
func functionalWrapper1(name string, fn FunctionalBuiltin1) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, 1)
@@ -197,7 +197,7 @@ func functionalWrapper1(name ast.String, fn FunctionalBuiltin1) BuiltinFunc {
}
}
func functionalWrapper2(name ast.String, fn FunctionalBuiltin2) BuiltinFunc {
func functionalWrapper2(name string, fn FunctionalBuiltin2) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, 2)
@@ -212,7 +212,7 @@ func functionalWrapper2(name ast.String, fn FunctionalBuiltin2) BuiltinFunc {
}
}
func functionalWrapper3(name ast.String, fn FunctionalBuiltin3) BuiltinFunc {
func functionalWrapper3(name string, fn FunctionalBuiltin3) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, 3)
@@ -227,7 +227,7 @@ func functionalWrapper3(name ast.String, fn FunctionalBuiltin3) BuiltinFunc {
}
}
func functionalWrapper1Out3(name ast.String, fn FunctionalBuiltin1Out3) BuiltinFunc {
func functionalWrapper1Out3(name string, fn FunctionalBuiltin1Out3) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, 1)
@@ -244,7 +244,7 @@ func functionalWrapper1Out3(name ast.String, fn FunctionalBuiltin1Out3) BuiltinF
}
}
func userFunctionWrapper(name ast.String, fns []*ast.Func) BuiltinFunc {
func userFunctionWrapper(name string, fns []*ast.Func) BuiltinFunc {
return func(t *Topdown, expr *ast.Expr, iter Iterator) error {
operands := expr.Terms.([]*ast.Term)[1:]
resolved, err := resolveN(t, name, operands, len(operands)-1)
@@ -299,7 +299,7 @@ func userFunctionWrapper(name ast.String, fns []*ast.Func) BuiltinFunc {
}
}
func handleFunctionalBuiltinErr(name ast.String, loc *ast.Location, err error) error {
func handleFunctionalBuiltinErr(name string, loc *ast.Location, err error) error {
switch err := err.(type) {
case BuiltinEmpty:
return nil
@@ -318,7 +318,7 @@ func handleFunctionalBuiltinErr(name ast.String, loc *ast.Location, err error) e
}
}
func resolveN(t *Topdown, name ast.String, ops []*ast.Term, n int) ([]ast.Value, error) {
func resolveN(t *Topdown, name string, ops []*ast.Term, n int) ([]ast.Value, error) {
result := make([]ast.Value, n)
for i := 0; i < n; i++ {
op, err := ResolveRefs(ops[i].Value, t)
+1 -1
View File
@@ -154,7 +154,7 @@ func ExampleRegisterFunctionalBuiltin1() {
// registry to include your built-in. Otherwise, the compiler will complain
// when it encounters your built-in.
builtin := &ast.Builtin{
Name: ast.String("mybuiltins.upper"),
Name: "mybuiltins.upper",
Args: []types.Type{
types.S,
types.S,
+5 -5
View File
@@ -37,7 +37,7 @@ type Topdown struct {
qid uint64
redos *redoStack
builtins builtins.Cache
userBuiltins map[ast.String]BuiltinFunc
userBuiltins map[string]BuiltinFunc
}
// ResetQueryIDs resets the query ID generator. This is only for test purposes.
@@ -90,7 +90,7 @@ func New(ctx context.Context, query ast.Body, compiler *ast.Compiler, store stor
qid: qidFactory.Next(),
redos: &redoStack{},
builtins: builtins.Cache{},
userBuiltins: map[ast.String]BuiltinFunc{},
userBuiltins: map[string]BuiltinFunc{},
}
t.registerUserFunctions()
return t
@@ -905,7 +905,7 @@ func evalExpr(t *Topdown, iter Iterator) error {
expr := PlugExpr(t.Current(), t.Binding)
switch tt := expr.Terms.(type) {
case []*ast.Term:
name := tt[0].Value.(ast.String)
name := tt[0].String()
builtin, ok := builtinFunctions[name]
if !ok {
builtin, ok = t.userBuiltins[name]
@@ -966,7 +966,7 @@ func evalRef(t *Topdown, ref, path ast.Ref, iter Iterator) error {
}
// This should not be reachable.
return fmt.Errorf("unbound ref head: %v", path)
return fmt.Errorf("unbound ref head")
}
var n ast.Ref
@@ -1882,7 +1882,7 @@ func evalTerms(t *Topdown, iter Iterator) error {
var ts []*ast.Term
switch t := expr.Terms.(type) {
case []*ast.Term:
ts = t
ts = t[1:]
case *ast.Term:
ts = append(ts, t)
default:
+10 -10
View File
@@ -1492,14 +1492,14 @@ func TestTopDownJWTBuiltins(t *testing.T) {
func TestTopDownTime(t *testing.T) {
ast.RegisterBuiltin(&ast.Builtin{
Name: ast.String("test_sleep"),
Name: "test_sleep",
Args: []types.Type{
types.S,
},
TargetPos: []int{1},
})
RegisterFunctionalBuiltinVoid1(ast.String("test_sleep"), func(a ast.Value) error {
RegisterFunctionalBuiltinVoid1("test_sleep", func(a ast.Value) error {
duration, err := time.ParseDuration(string(a.(ast.String)))
if err != nil {
panic(err)
@@ -1783,8 +1783,7 @@ func TestTopDownPartialDocConstants(t *testing.T) {
}
func TestTopDownUserFunc(t *testing.T) {
compiler := compileModules([]string{
`package ex
modules := []string{`package ex
foo(x) = y {
split(x, "i", y)
@@ -1911,8 +1910,9 @@ func TestTopDownUserFunc(t *testing.T) {
samepkg = y {
foo("how do you do?", y)
}`,
})
}`}
compiler := compileModules(modules)
store := inmem.NewFromObject(loadSmallTestData())
ctx := context.Background()
txn := storage.NewTransactionOrDie(ctx, store)
@@ -1985,9 +1985,9 @@ func TestUserFunctionErrors(t *testing.T) {
txn := storage.NewTransactionOrDie(ctx, store)
defer store.Abort(ctx, txn)
assertTopDownWithPath(t, compiler, store, "function output conflict single", []string{"test1", "r"}, "", errors.New(`eval_conflict_error: function "test1.p" produces conflicting outputs`))
assertTopDownWithPath(t, compiler, store, "function output conflict single", []string{"test1", "r"}, "", errors.New(`eval_conflict_error: function test1.p produces conflicting outputs`))
assertTopDownWithPath(t, compiler, store, "function input no match", []string{"test2", "r"}, "", "")
assertTopDownWithPath(t, compiler, store, "function output conflict multiple", []string{"test3", "r"}, "", errors.New(`eval_conflict_error: function "test3.p" produces conflicting outputs`))
assertTopDownWithPath(t, compiler, store, "function output conflict multiple", []string{"test3", "r"}, "", errors.New(`eval_conflict_error: function test3.p produces conflicting outputs`))
}
func TestTopDownWithKeyword(t *testing.T) {
@@ -2240,7 +2240,7 @@ violations[server] { server = servers[_]; server.protocols[_] = "http"; public_s
func TestTopDownUnsupportedBuiltin(t *testing.T) {
ast.RegisterBuiltin(&ast.Builtin{
Name: ast.String("unsupported_builtin"),
Name: "unsupported_builtin",
})
body := ast.MustParseBody(`unsupported_builtin()`)
@@ -2264,7 +2264,7 @@ func TestTopDownUnsupportedBuiltin(t *testing.T) {
func TestTopDownQueryCancellation(t *testing.T) {
ast.RegisterBuiltin(&ast.Builtin{
Name: ast.String("test.sleep"),
Name: "test.sleep",
Args: []types.Type{
types.S,
},
+1 -1
View File
@@ -228,7 +228,7 @@ func TestWatchMigrateInvalidate(t *testing.T) {
second := <-handle.C
expSecond := Event{
Query: `x = data.y.r["foo"]+1`,
Error: errors.New(`watch invalidated: 1 error occurred: 1:1: rego_type_error: "plus": invalid argument(s)
Error: errors.New(`watch invalidated: 1 error occurred: 1:1: rego_type_error: plus: invalid argument(s)
have: (string, number, ???)
want: (number, number, number)`),
}