fix: upgrade github.com/open-policy-agent/opa v0.70.0 => v1.4.0 (#6510)
Signed-off-by: peng wu <2030047311@qq.com>
This commit is contained in:
962
vendor/github.com/open-policy-agent/opa/ast/annotations.go
generated
vendored
962
vendor/github.com/open-policy-agent/opa/ast/annotations.go
generated
vendored
@@ -5,973 +5,33 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
astJSON "github.com/open-policy-agent/opa/ast/json"
|
||||
"github.com/open-policy-agent/opa/internal/deepcopy"
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
)
|
||||
|
||||
const (
|
||||
annotationScopePackage = "package"
|
||||
annotationScopeImport = "import"
|
||||
annotationScopeRule = "rule"
|
||||
annotationScopeDocument = "document"
|
||||
annotationScopeSubpackages = "subpackages"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
type (
|
||||
// Annotations represents metadata attached to other AST nodes such as rules.
|
||||
Annotations struct {
|
||||
Scope string `json:"scope"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Entrypoint bool `json:"entrypoint,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Organizations []string `json:"organizations,omitempty"`
|
||||
RelatedResources []*RelatedResourceAnnotation `json:"related_resources,omitempty"`
|
||||
Authors []*AuthorAnnotation `json:"authors,omitempty"`
|
||||
Schemas []*SchemaAnnotation `json:"schemas,omitempty"`
|
||||
Custom map[string]interface{} `json:"custom,omitempty"`
|
||||
Location *Location `json:"location,omitempty"`
|
||||
|
||||
comments []*Comment
|
||||
node Node
|
||||
jsonOptions astJSON.Options
|
||||
}
|
||||
Annotations = v1.Annotations
|
||||
|
||||
// SchemaAnnotation contains a schema declaration for the document identified by the path.
|
||||
SchemaAnnotation struct {
|
||||
Path Ref `json:"path"`
|
||||
Schema Ref `json:"schema,omitempty"`
|
||||
Definition *interface{} `json:"definition,omitempty"`
|
||||
}
|
||||
SchemaAnnotation = v1.SchemaAnnotation
|
||||
|
||||
AuthorAnnotation struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
AuthorAnnotation = v1.AuthorAnnotation
|
||||
|
||||
RelatedResourceAnnotation struct {
|
||||
Ref url.URL `json:"ref"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
RelatedResourceAnnotation = v1.RelatedResourceAnnotation
|
||||
|
||||
AnnotationSet struct {
|
||||
byRule map[*Rule][]*Annotations
|
||||
byPackage map[int]*Annotations
|
||||
byPath *annotationTreeNode
|
||||
modules []*Module // Modules this set was constructed from
|
||||
}
|
||||
AnnotationSet = v1.AnnotationSet
|
||||
|
||||
annotationTreeNode struct {
|
||||
Value *Annotations
|
||||
Children map[Value]*annotationTreeNode // we assume key elements are hashable (vars and strings only!)
|
||||
}
|
||||
AnnotationsRef = v1.AnnotationsRef
|
||||
|
||||
AnnotationsRef struct {
|
||||
Path Ref `json:"path"` // The path of the node the annotations are applied to
|
||||
Annotations *Annotations `json:"annotations,omitempty"`
|
||||
Location *Location `json:"location,omitempty"` // The location of the node the annotations are applied to
|
||||
AnnotationsRefSet = v1.AnnotationsRefSet
|
||||
|
||||
jsonOptions astJSON.Options
|
||||
|
||||
node Node // The node the annotations are applied to
|
||||
}
|
||||
|
||||
AnnotationsRefSet []*AnnotationsRef
|
||||
|
||||
FlatAnnotationsRefSet AnnotationsRefSet
|
||||
FlatAnnotationsRefSet = v1.FlatAnnotationsRefSet
|
||||
)
|
||||
|
||||
func (a *Annotations) String() string {
|
||||
bs, _ := a.MarshalJSON()
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
// Loc returns the location of this annotation.
|
||||
func (a *Annotations) Loc() *Location {
|
||||
return a.Location
|
||||
}
|
||||
|
||||
// SetLoc updates the location of this annotation.
|
||||
func (a *Annotations) SetLoc(l *Location) {
|
||||
a.Location = l
|
||||
}
|
||||
|
||||
// EndLoc returns the location of this annotation's last comment line.
|
||||
func (a *Annotations) EndLoc() *Location {
|
||||
count := len(a.comments)
|
||||
if count == 0 {
|
||||
return a.Location
|
||||
}
|
||||
return a.comments[count-1].Location
|
||||
}
|
||||
|
||||
// Compare returns an integer indicating if a is less than, equal to, or greater
|
||||
// than other.
|
||||
func (a *Annotations) Compare(other *Annotations) int {
|
||||
|
||||
if a == nil && other == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
if other == nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
if cmp := scopeCompare(a.Scope, other.Scope); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := strings.Compare(a.Title, other.Title); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := strings.Compare(a.Description, other.Description); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := compareStringLists(a.Organizations, other.Organizations); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := compareRelatedResources(a.RelatedResources, other.RelatedResources); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := compareAuthors(a.Authors, other.Authors); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := compareSchemas(a.Schemas, other.Schemas); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if a.Entrypoint != other.Entrypoint {
|
||||
if a.Entrypoint {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
if cmp := util.Compare(a.Custom, other.Custom); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetTargetPath returns the path of the node these Annotations are applied to (the target)
|
||||
func (a *Annotations) GetTargetPath() Ref {
|
||||
switch n := a.node.(type) {
|
||||
case *Package:
|
||||
return n.Path
|
||||
case *Rule:
|
||||
return n.Ref().GroundPrefix()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Annotations) setJSONOptions(opts astJSON.Options) {
|
||||
a.jsonOptions = opts
|
||||
if a.Location != nil {
|
||||
a.Location.JSONOptions = opts
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Annotations) MarshalJSON() ([]byte, error) {
|
||||
if a == nil {
|
||||
return []byte(`{"scope":""}`), nil
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"scope": a.Scope,
|
||||
}
|
||||
|
||||
if a.Title != "" {
|
||||
data["title"] = a.Title
|
||||
}
|
||||
|
||||
if a.Description != "" {
|
||||
data["description"] = a.Description
|
||||
}
|
||||
|
||||
if a.Entrypoint {
|
||||
data["entrypoint"] = a.Entrypoint
|
||||
}
|
||||
|
||||
if len(a.Organizations) > 0 {
|
||||
data["organizations"] = a.Organizations
|
||||
}
|
||||
|
||||
if len(a.RelatedResources) > 0 {
|
||||
data["related_resources"] = a.RelatedResources
|
||||
}
|
||||
|
||||
if len(a.Authors) > 0 {
|
||||
data["authors"] = a.Authors
|
||||
}
|
||||
|
||||
if len(a.Schemas) > 0 {
|
||||
data["schemas"] = a.Schemas
|
||||
}
|
||||
|
||||
if len(a.Custom) > 0 {
|
||||
data["custom"] = a.Custom
|
||||
}
|
||||
|
||||
if a.jsonOptions.MarshalOptions.IncludeLocation.Annotations {
|
||||
if a.Location != nil {
|
||||
data["location"] = a.Location
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
func NewAnnotationsRef(a *Annotations) *AnnotationsRef {
|
||||
var loc *Location
|
||||
if a.node != nil {
|
||||
loc = a.node.Loc()
|
||||
}
|
||||
|
||||
return &AnnotationsRef{
|
||||
Location: loc,
|
||||
Path: a.GetTargetPath(),
|
||||
Annotations: a,
|
||||
node: a.node,
|
||||
jsonOptions: a.jsonOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *AnnotationsRef) GetPackage() *Package {
|
||||
switch n := ar.node.(type) {
|
||||
case *Package:
|
||||
return n
|
||||
case *Rule:
|
||||
return n.Module.Package
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *AnnotationsRef) GetRule() *Rule {
|
||||
switch n := ar.node.(type) {
|
||||
case *Rule:
|
||||
return n
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *AnnotationsRef) MarshalJSON() ([]byte, error) {
|
||||
data := map[string]interface{}{
|
||||
"path": ar.Path,
|
||||
}
|
||||
|
||||
if ar.Annotations != nil {
|
||||
data["annotations"] = ar.Annotations
|
||||
}
|
||||
|
||||
if ar.jsonOptions.MarshalOptions.IncludeLocation.AnnotationsRef {
|
||||
if ar.Location != nil {
|
||||
data["location"] = ar.Location
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
func scopeCompare(s1, s2 string) int {
|
||||
|
||||
o1 := scopeOrder(s1)
|
||||
o2 := scopeOrder(s2)
|
||||
|
||||
if o2 < o1 {
|
||||
return 1
|
||||
} else if o2 > o1 {
|
||||
return -1
|
||||
}
|
||||
|
||||
if s1 < s2 {
|
||||
return -1
|
||||
} else if s2 < s1 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func scopeOrder(s string) int {
|
||||
switch s {
|
||||
case annotationScopeRule:
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareAuthors(a, b []*AuthorAnnotation) int {
|
||||
if len(a) > len(b) {
|
||||
return 1
|
||||
} else if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareRelatedResources(a, b []*RelatedResourceAnnotation) int {
|
||||
if len(a) > len(b) {
|
||||
return 1
|
||||
} else if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if cmp := strings.Compare(a[i].String(), b[i].String()); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareSchemas(a, b []*SchemaAnnotation) int {
|
||||
maxLen := len(a)
|
||||
if len(b) < maxLen {
|
||||
maxLen = len(b)
|
||||
}
|
||||
|
||||
for i := 0; i < maxLen; i++ {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
|
||||
if len(a) > len(b) {
|
||||
return 1
|
||||
} else if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareStringLists(a, b []string) int {
|
||||
if len(a) > len(b) {
|
||||
return 1
|
||||
} else if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if cmp := strings.Compare(a[i], b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of s.
|
||||
func (a *Annotations) Copy(node Node) *Annotations {
|
||||
cpy := *a
|
||||
|
||||
cpy.Organizations = make([]string, len(a.Organizations))
|
||||
copy(cpy.Organizations, a.Organizations)
|
||||
|
||||
cpy.RelatedResources = make([]*RelatedResourceAnnotation, len(a.RelatedResources))
|
||||
for i := range a.RelatedResources {
|
||||
cpy.RelatedResources[i] = a.RelatedResources[i].Copy()
|
||||
}
|
||||
|
||||
cpy.Authors = make([]*AuthorAnnotation, len(a.Authors))
|
||||
for i := range a.Authors {
|
||||
cpy.Authors[i] = a.Authors[i].Copy()
|
||||
}
|
||||
|
||||
cpy.Schemas = make([]*SchemaAnnotation, len(a.Schemas))
|
||||
for i := range a.Schemas {
|
||||
cpy.Schemas[i] = a.Schemas[i].Copy()
|
||||
}
|
||||
|
||||
cpy.Custom = deepcopy.Map(a.Custom)
|
||||
|
||||
cpy.node = node
|
||||
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// toObject constructs an AST Object from the annotation.
|
||||
func (a *Annotations) toObject() (*Object, *Error) {
|
||||
obj := NewObject()
|
||||
|
||||
if a == nil {
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
if len(a.Scope) > 0 {
|
||||
obj.Insert(StringTerm("scope"), StringTerm(a.Scope))
|
||||
}
|
||||
|
||||
if len(a.Title) > 0 {
|
||||
obj.Insert(StringTerm("title"), StringTerm(a.Title))
|
||||
}
|
||||
|
||||
if a.Entrypoint {
|
||||
obj.Insert(StringTerm("entrypoint"), BooleanTerm(true))
|
||||
}
|
||||
|
||||
if len(a.Description) > 0 {
|
||||
obj.Insert(StringTerm("description"), StringTerm(a.Description))
|
||||
}
|
||||
|
||||
if len(a.Organizations) > 0 {
|
||||
orgs := make([]*Term, 0, len(a.Organizations))
|
||||
for _, org := range a.Organizations {
|
||||
orgs = append(orgs, StringTerm(org))
|
||||
}
|
||||
obj.Insert(StringTerm("organizations"), ArrayTerm(orgs...))
|
||||
}
|
||||
|
||||
if len(a.RelatedResources) > 0 {
|
||||
rrs := make([]*Term, 0, len(a.RelatedResources))
|
||||
for _, rr := range a.RelatedResources {
|
||||
rrObj := NewObject(Item(StringTerm("ref"), StringTerm(rr.Ref.String())))
|
||||
if len(rr.Description) > 0 {
|
||||
rrObj.Insert(StringTerm("description"), StringTerm(rr.Description))
|
||||
}
|
||||
rrs = append(rrs, NewTerm(rrObj))
|
||||
}
|
||||
obj.Insert(StringTerm("related_resources"), ArrayTerm(rrs...))
|
||||
}
|
||||
|
||||
if len(a.Authors) > 0 {
|
||||
as := make([]*Term, 0, len(a.Authors))
|
||||
for _, author := range a.Authors {
|
||||
aObj := NewObject()
|
||||
if len(author.Name) > 0 {
|
||||
aObj.Insert(StringTerm("name"), StringTerm(author.Name))
|
||||
}
|
||||
if len(author.Email) > 0 {
|
||||
aObj.Insert(StringTerm("email"), StringTerm(author.Email))
|
||||
}
|
||||
as = append(as, NewTerm(aObj))
|
||||
}
|
||||
obj.Insert(StringTerm("authors"), ArrayTerm(as...))
|
||||
}
|
||||
|
||||
if len(a.Schemas) > 0 {
|
||||
ss := make([]*Term, 0, len(a.Schemas))
|
||||
for _, s := range a.Schemas {
|
||||
sObj := NewObject()
|
||||
if len(s.Path) > 0 {
|
||||
sObj.Insert(StringTerm("path"), NewTerm(s.Path.toArray()))
|
||||
}
|
||||
if len(s.Schema) > 0 {
|
||||
sObj.Insert(StringTerm("schema"), NewTerm(s.Schema.toArray()))
|
||||
}
|
||||
if s.Definition != nil {
|
||||
def, err := InterfaceToValue(s.Definition)
|
||||
if err != nil {
|
||||
return nil, NewError(CompileErr, a.Location, "invalid definition in schema annotation: %s", err.Error())
|
||||
}
|
||||
sObj.Insert(StringTerm("definition"), NewTerm(def))
|
||||
}
|
||||
ss = append(ss, NewTerm(sObj))
|
||||
}
|
||||
obj.Insert(StringTerm("schemas"), ArrayTerm(ss...))
|
||||
}
|
||||
|
||||
if len(a.Custom) > 0 {
|
||||
c, err := InterfaceToValue(a.Custom)
|
||||
if err != nil {
|
||||
return nil, NewError(CompileErr, a.Location, "invalid custom annotation %s", err.Error())
|
||||
}
|
||||
obj.Insert(StringTerm("custom"), NewTerm(c))
|
||||
}
|
||||
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
func attachRuleAnnotations(mod *Module) {
|
||||
// make a copy of the annotations
|
||||
cpy := make([]*Annotations, len(mod.Annotations))
|
||||
for i, a := range mod.Annotations {
|
||||
cpy[i] = a.Copy(a.node)
|
||||
}
|
||||
|
||||
for _, rule := range mod.Rules {
|
||||
var j int
|
||||
var found bool
|
||||
for i, a := range cpy {
|
||||
if rule.Ref().GroundPrefix().Equal(a.GetTargetPath()) {
|
||||
if a.Scope == annotationScopeDocument {
|
||||
rule.Annotations = append(rule.Annotations, a)
|
||||
} else if a.Scope == annotationScopeRule && rule.Loc().Row > a.Location.Row {
|
||||
j = i
|
||||
found = true
|
||||
rule.Annotations = append(rule.Annotations, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found && j < len(cpy) {
|
||||
cpy = append(cpy[:j], cpy[j+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func attachAnnotationsNodes(mod *Module) Errors {
|
||||
var errs Errors
|
||||
|
||||
// Find first non-annotation statement following each annotation and attach
|
||||
// the annotation to that statement.
|
||||
for _, a := range mod.Annotations {
|
||||
for _, stmt := range mod.stmts {
|
||||
_, ok := stmt.(*Annotations)
|
||||
if !ok {
|
||||
if stmt.Loc().Row > a.Location.Row {
|
||||
a.node = stmt
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if a.Scope == "" {
|
||||
switch a.node.(type) {
|
||||
case *Rule:
|
||||
if a.Entrypoint {
|
||||
a.Scope = annotationScopeDocument
|
||||
} else {
|
||||
a.Scope = annotationScopeRule
|
||||
}
|
||||
case *Package:
|
||||
a.Scope = annotationScopePackage
|
||||
case *Import:
|
||||
a.Scope = annotationScopeImport
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateAnnotationScopeAttachment(a); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := validateAnnotationEntrypointAttachment(a); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateAnnotationScopeAttachment(a *Annotations) *Error {
|
||||
|
||||
switch a.Scope {
|
||||
case annotationScopeRule, annotationScopeDocument:
|
||||
if _, ok := a.node.(*Rule); ok {
|
||||
return nil
|
||||
}
|
||||
return newScopeAttachmentErr(a, "rule")
|
||||
case annotationScopePackage, annotationScopeSubpackages:
|
||||
if _, ok := a.node.(*Package); ok {
|
||||
return nil
|
||||
}
|
||||
return newScopeAttachmentErr(a, "package")
|
||||
}
|
||||
|
||||
return NewError(ParseErr, a.Loc(), "invalid annotation scope '%v'. Use one of '%s', '%s', '%s', or '%s'",
|
||||
a.Scope, annotationScopeRule, annotationScopeDocument, annotationScopePackage, annotationScopeSubpackages)
|
||||
}
|
||||
|
||||
func validateAnnotationEntrypointAttachment(a *Annotations) *Error {
|
||||
if a.Entrypoint && !(a.Scope == annotationScopeDocument || a.Scope == annotationScopePackage) {
|
||||
return NewError(
|
||||
ParseErr, a.Loc(), "annotation entrypoint applied to non-document or package scope '%v'", a.Scope)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of a.
|
||||
func (a *AuthorAnnotation) Copy() *AuthorAnnotation {
|
||||
cpy := *a
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Compare returns an integer indicating if s is less than, equal to, or greater
|
||||
// than other.
|
||||
func (a *AuthorAnnotation) Compare(other *AuthorAnnotation) int {
|
||||
if cmp := strings.Compare(a.Name, other.Name); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := strings.Compare(a.Email, other.Email); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (a *AuthorAnnotation) String() string {
|
||||
if len(a.Email) == 0 {
|
||||
return a.Name
|
||||
} else if len(a.Name) == 0 {
|
||||
return fmt.Sprintf("<%s>", a.Email)
|
||||
}
|
||||
return fmt.Sprintf("%s <%s>", a.Name, a.Email)
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of rr.
|
||||
func (rr *RelatedResourceAnnotation) Copy() *RelatedResourceAnnotation {
|
||||
cpy := *rr
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Compare returns an integer indicating if s is less than, equal to, or greater
|
||||
// than other.
|
||||
func (rr *RelatedResourceAnnotation) Compare(other *RelatedResourceAnnotation) int {
|
||||
if cmp := strings.Compare(rr.Description, other.Description); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := strings.Compare(rr.Ref.String(), other.Ref.String()); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rr *RelatedResourceAnnotation) String() string {
|
||||
bs, _ := json.Marshal(rr)
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
func (rr *RelatedResourceAnnotation) MarshalJSON() ([]byte, error) {
|
||||
d := map[string]interface{}{
|
||||
"ref": rr.Ref.String(),
|
||||
}
|
||||
|
||||
if len(rr.Description) > 0 {
|
||||
d["description"] = rr.Description
|
||||
}
|
||||
|
||||
return json.Marshal(d)
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of s.
|
||||
func (s *SchemaAnnotation) Copy() *SchemaAnnotation {
|
||||
cpy := *s
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Compare returns an integer indicating if s is less than, equal to, or greater
|
||||
// than other.
|
||||
func (s *SchemaAnnotation) Compare(other *SchemaAnnotation) int {
|
||||
|
||||
if cmp := s.Path.Compare(other.Path); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if cmp := s.Schema.Compare(other.Schema); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
if s.Definition != nil && other.Definition == nil {
|
||||
return -1
|
||||
} else if s.Definition == nil && other.Definition != nil {
|
||||
return 1
|
||||
} else if s.Definition != nil && other.Definition != nil {
|
||||
return util.Compare(*s.Definition, *other.Definition)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *SchemaAnnotation) String() string {
|
||||
bs, _ := json.Marshal(s)
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
func newAnnotationSet() *AnnotationSet {
|
||||
return &AnnotationSet{
|
||||
byRule: map[*Rule][]*Annotations{},
|
||||
byPackage: map[int]*Annotations{},
|
||||
byPath: newAnnotationTree(),
|
||||
}
|
||||
return v1.NewAnnotationsRef(a)
|
||||
}
|
||||
|
||||
func BuildAnnotationSet(modules []*Module) (*AnnotationSet, Errors) {
|
||||
as := newAnnotationSet()
|
||||
var errs Errors
|
||||
for _, m := range modules {
|
||||
for _, a := range m.Annotations {
|
||||
if err := as.add(a); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
as.modules = modules
|
||||
return as, nil
|
||||
}
|
||||
|
||||
// NOTE(philipc): During copy propagation, the underlying Nodes can be
|
||||
// stripped away from the annotations, leading to nil deref panics. We
|
||||
// silently ignore these cases for now, as a workaround.
|
||||
func (as *AnnotationSet) add(a *Annotations) *Error {
|
||||
switch a.Scope {
|
||||
case annotationScopeRule:
|
||||
if rule, ok := a.node.(*Rule); ok {
|
||||
as.byRule[rule] = append(as.byRule[rule], a)
|
||||
}
|
||||
case annotationScopePackage:
|
||||
if pkg, ok := a.node.(*Package); ok {
|
||||
hash := pkg.Path.Hash()
|
||||
if exist, ok := as.byPackage[hash]; ok {
|
||||
return errAnnotationRedeclared(a, exist.Location)
|
||||
}
|
||||
as.byPackage[hash] = a
|
||||
}
|
||||
case annotationScopeDocument:
|
||||
if rule, ok := a.node.(*Rule); ok {
|
||||
path := rule.Ref().GroundPrefix()
|
||||
x := as.byPath.get(path)
|
||||
if x != nil {
|
||||
return errAnnotationRedeclared(a, x.Value.Location)
|
||||
}
|
||||
as.byPath.insert(path, a)
|
||||
}
|
||||
case annotationScopeSubpackages:
|
||||
if pkg, ok := a.node.(*Package); ok {
|
||||
x := as.byPath.get(pkg.Path)
|
||||
if x != nil && x.Value != nil {
|
||||
return errAnnotationRedeclared(a, x.Value.Location)
|
||||
}
|
||||
as.byPath.insert(pkg.Path, a)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *AnnotationSet) GetRuleScope(r *Rule) []*Annotations {
|
||||
if as == nil {
|
||||
return nil
|
||||
}
|
||||
return as.byRule[r]
|
||||
}
|
||||
|
||||
func (as *AnnotationSet) GetSubpackagesScope(path Ref) []*Annotations {
|
||||
if as == nil {
|
||||
return nil
|
||||
}
|
||||
return as.byPath.ancestors(path)
|
||||
}
|
||||
|
||||
func (as *AnnotationSet) GetDocumentScope(path Ref) *Annotations {
|
||||
if as == nil {
|
||||
return nil
|
||||
}
|
||||
if node := as.byPath.get(path); node != nil {
|
||||
return node.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *AnnotationSet) GetPackageScope(pkg *Package) *Annotations {
|
||||
if as == nil {
|
||||
return nil
|
||||
}
|
||||
return as.byPackage[pkg.Path.Hash()]
|
||||
}
|
||||
|
||||
// Flatten returns a flattened list view of this AnnotationSet.
|
||||
// The returned slice is sorted, first by the annotations' target path, then by their target location
|
||||
func (as *AnnotationSet) Flatten() FlatAnnotationsRefSet {
|
||||
// This preallocation often won't be optimal, but it's superior to starting with a nil slice.
|
||||
refs := make([]*AnnotationsRef, 0, len(as.byPath.Children)+len(as.byRule)+len(as.byPackage))
|
||||
|
||||
refs = as.byPath.flatten(refs)
|
||||
|
||||
for _, a := range as.byPackage {
|
||||
refs = append(refs, NewAnnotationsRef(a))
|
||||
}
|
||||
|
||||
for _, as := range as.byRule {
|
||||
for _, a := range as {
|
||||
refs = append(refs, NewAnnotationsRef(a))
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by path, then annotation location, for stable output
|
||||
sort.SliceStable(refs, func(i, j int) bool {
|
||||
return refs[i].Compare(refs[j]) < 0
|
||||
})
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
// Chain returns the chain of annotations leading up to the given rule.
|
||||
// The returned slice is ordered as follows
|
||||
// 0. Entries for the given rule, ordered from the METADATA block declared immediately above the rule, to the block declared farthest away (always at least one entry)
|
||||
// 1. The 'document' scope entry, if any
|
||||
// 2. The 'package' scope entry, if any
|
||||
// 3. Entries for the 'subpackages' scope, if any; ordered from the closest package path to the fartest. E.g.: 'do.re.mi', 'do.re', 'do'
|
||||
// The returned slice is guaranteed to always contain at least one entry, corresponding to the given rule.
|
||||
func (as *AnnotationSet) Chain(rule *Rule) AnnotationsRefSet {
|
||||
var refs []*AnnotationsRef
|
||||
|
||||
ruleAnnots := as.GetRuleScope(rule)
|
||||
|
||||
if len(ruleAnnots) >= 1 {
|
||||
for _, a := range ruleAnnots {
|
||||
refs = append(refs, NewAnnotationsRef(a))
|
||||
}
|
||||
} else {
|
||||
// Make sure there is always a leading entry representing the passed rule, even if it has no annotations
|
||||
refs = append(refs, &AnnotationsRef{
|
||||
Location: rule.Location,
|
||||
Path: rule.Ref().GroundPrefix(),
|
||||
node: rule,
|
||||
})
|
||||
}
|
||||
|
||||
if len(refs) > 1 {
|
||||
// Sort by annotation location; chain must start with annotations declared closest to rule, then going outward
|
||||
sort.SliceStable(refs, func(i, j int) bool {
|
||||
return refs[i].Annotations.Location.Compare(refs[j].Annotations.Location) > 0
|
||||
})
|
||||
}
|
||||
|
||||
docAnnots := as.GetDocumentScope(rule.Ref().GroundPrefix())
|
||||
if docAnnots != nil {
|
||||
refs = append(refs, NewAnnotationsRef(docAnnots))
|
||||
}
|
||||
|
||||
pkg := rule.Module.Package
|
||||
pkgAnnots := as.GetPackageScope(pkg)
|
||||
if pkgAnnots != nil {
|
||||
refs = append(refs, NewAnnotationsRef(pkgAnnots))
|
||||
}
|
||||
|
||||
subPkgAnnots := as.GetSubpackagesScope(pkg.Path)
|
||||
// We need to reverse the order, as subPkgAnnots ordering will start at the root,
|
||||
// whereas we want to end at the root.
|
||||
for i := len(subPkgAnnots) - 1; i >= 0; i-- {
|
||||
refs = append(refs, NewAnnotationsRef(subPkgAnnots[i]))
|
||||
}
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
func (ars FlatAnnotationsRefSet) Insert(ar *AnnotationsRef) FlatAnnotationsRefSet {
|
||||
result := make(FlatAnnotationsRefSet, 0, len(ars)+1)
|
||||
|
||||
// insertion sort, first by path, then location
|
||||
for i, current := range ars {
|
||||
if ar.Compare(current) < 0 {
|
||||
result = append(result, ar)
|
||||
result = append(result, ars[i:]...)
|
||||
break
|
||||
}
|
||||
result = append(result, current)
|
||||
}
|
||||
|
||||
if len(result) < len(ars)+1 {
|
||||
result = append(result, ar)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func newAnnotationTree() *annotationTreeNode {
|
||||
return &annotationTreeNode{
|
||||
Value: nil,
|
||||
Children: map[Value]*annotationTreeNode{},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *annotationTreeNode) insert(path Ref, value *Annotations) {
|
||||
node := t
|
||||
for _, k := range path {
|
||||
child, ok := node.Children[k.Value]
|
||||
if !ok {
|
||||
child = newAnnotationTree()
|
||||
node.Children[k.Value] = child
|
||||
}
|
||||
node = child
|
||||
}
|
||||
node.Value = value
|
||||
}
|
||||
|
||||
func (t *annotationTreeNode) get(path Ref) *annotationTreeNode {
|
||||
node := t
|
||||
for _, k := range path {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
child, ok := node.Children[k.Value]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
node = child
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// ancestors returns a slice of annotations in ascending order, starting with the root of ref; e.g.: 'root', 'root.foo', 'root.foo.bar'.
|
||||
func (t *annotationTreeNode) ancestors(path Ref) (result []*Annotations) {
|
||||
node := t
|
||||
for _, k := range path {
|
||||
if node == nil {
|
||||
return result
|
||||
}
|
||||
child, ok := node.Children[k.Value]
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if child.Value != nil {
|
||||
result = append(result, child.Value)
|
||||
}
|
||||
node = child
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (t *annotationTreeNode) flatten(refs []*AnnotationsRef) []*AnnotationsRef {
|
||||
if a := t.Value; a != nil {
|
||||
refs = append(refs, NewAnnotationsRef(a))
|
||||
}
|
||||
for _, c := range t.Children {
|
||||
refs = c.flatten(refs)
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
func (ar *AnnotationsRef) Compare(other *AnnotationsRef) int {
|
||||
if c := ar.Path.Compare(other.Path); c != 0 {
|
||||
return c
|
||||
}
|
||||
|
||||
if c := ar.Annotations.Location.Compare(other.Annotations.Location); c != 0 {
|
||||
return c
|
||||
}
|
||||
|
||||
return ar.Annotations.Compare(other.Annotations)
|
||||
return v1.BuildAnnotationSet(modules)
|
||||
}
|
||||
|
||||
3153
vendor/github.com/open-policy-agent/opa/ast/builtins.go
generated
vendored
3153
vendor/github.com/open-policy-agent/opa/ast/builtins.go
generated
vendored
File diff suppressed because it is too large
Load Diff
200
vendor/github.com/open-policy-agent/opa/ast/capabilities.go
generated
vendored
200
vendor/github.com/open-policy-agent/opa/ast/capabilities.go
generated
vendored
@@ -5,228 +5,54 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
caps "github.com/open-policy-agent/opa/capabilities"
|
||||
"github.com/open-policy-agent/opa/internal/semver"
|
||||
"github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities"
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// VersonIndex contains an index from built-in function name, language feature,
|
||||
// and future rego keyword to version number. During the build, this is used to
|
||||
// create an index of the minimum version required for the built-in/feature/kw.
|
||||
type VersionIndex struct {
|
||||
Builtins map[string]semver.Version `json:"builtins"`
|
||||
Features map[string]semver.Version `json:"features"`
|
||||
Keywords map[string]semver.Version `json:"keywords"`
|
||||
}
|
||||
|
||||
// NOTE(tsandall): this file is generated by internal/cmd/genversionindex/main.go
|
||||
// and run as part of go:generate. We generate the version index as part of the
|
||||
// build process because it's relatively expensive to build (it takes ~500ms on
|
||||
// my machine) and never changes.
|
||||
//
|
||||
//go:embed version_index.json
|
||||
var versionIndexBs []byte
|
||||
|
||||
var minVersionIndex = func() VersionIndex {
|
||||
var vi VersionIndex
|
||||
err := json.Unmarshal(versionIndexBs, &vi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vi
|
||||
}()
|
||||
type VersionIndex = v1.VersionIndex
|
||||
|
||||
// In the compiler, we used this to check that we're OK working with ref heads.
|
||||
// If this isn't present, we'll fail. This is to ensure that older versions of
|
||||
// OPA can work with policies that we're compiling -- if they don't know ref
|
||||
// heads, they wouldn't be able to parse them.
|
||||
const FeatureRefHeadStringPrefixes = "rule_head_ref_string_prefixes"
|
||||
const FeatureRefHeads = "rule_head_refs"
|
||||
const FeatureRegoV1Import = "rego_v1_import"
|
||||
const FeatureRefHeadStringPrefixes = v1.FeatureRefHeadStringPrefixes
|
||||
const FeatureRefHeads = v1.FeatureRefHeads
|
||||
const FeatureRegoV1 = v1.FeatureRegoV1
|
||||
const FeatureRegoV1Import = v1.FeatureRegoV1Import
|
||||
|
||||
// Capabilities defines a structure containing data that describes the capabilities
|
||||
// or features supported by a particular version of OPA.
|
||||
type Capabilities struct {
|
||||
Builtins []*Builtin `json:"builtins,omitempty"`
|
||||
FutureKeywords []string `json:"future_keywords,omitempty"`
|
||||
WasmABIVersions []WasmABIVersion `json:"wasm_abi_versions,omitempty"`
|
||||
|
||||
// Features is a bit of a mixed bag for checking that an older version of OPA
|
||||
// is able to do what needs to be done.
|
||||
// TODO(sr): find better words ^^
|
||||
Features []string `json:"features,omitempty"`
|
||||
|
||||
// allow_net is an array of hostnames or IP addresses, that an OPA instance is
|
||||
// allowed to connect to.
|
||||
// If omitted, ANY host can be connected to. If empty, NO host can be connected to.
|
||||
// As of now, this only controls fetching remote refs for using JSON Schemas in
|
||||
// the type checker.
|
||||
// TODO(sr): support ports to further restrict connection peers
|
||||
// TODO(sr): support restricting `http.send` using the same mechanism (see https://github.com/open-policy-agent/opa/issues/3665)
|
||||
AllowNet []string `json:"allow_net,omitempty"`
|
||||
}
|
||||
type Capabilities = v1.Capabilities
|
||||
|
||||
// WasmABIVersion captures the Wasm ABI version. Its `Minor` version is indicating
|
||||
// backwards-compatible changes.
|
||||
type WasmABIVersion struct {
|
||||
Version int `json:"version"`
|
||||
Minor int `json:"minor_version"`
|
||||
}
|
||||
type WasmABIVersion = v1.WasmABIVersion
|
||||
|
||||
// CapabilitiesForThisVersion returns the capabilities of this version of OPA.
|
||||
func CapabilitiesForThisVersion() *Capabilities {
|
||||
f := &Capabilities{}
|
||||
|
||||
for _, vers := range capabilities.ABIVersions() {
|
||||
f.WasmABIVersions = append(f.WasmABIVersions, WasmABIVersion{Version: vers[0], Minor: vers[1]})
|
||||
}
|
||||
|
||||
f.Builtins = make([]*Builtin, len(Builtins))
|
||||
copy(f.Builtins, Builtins)
|
||||
sort.Slice(f.Builtins, func(i, j int) bool {
|
||||
return f.Builtins[i].Name < f.Builtins[j].Name
|
||||
})
|
||||
|
||||
for kw := range futureKeywords {
|
||||
f.FutureKeywords = append(f.FutureKeywords, kw)
|
||||
}
|
||||
sort.Strings(f.FutureKeywords)
|
||||
|
||||
f.Features = []string{
|
||||
FeatureRefHeadStringPrefixes,
|
||||
FeatureRefHeads,
|
||||
FeatureRegoV1Import,
|
||||
}
|
||||
|
||||
return f
|
||||
return v1.CapabilitiesForThisVersion(v1.CapabilitiesRegoVersion(DefaultRegoVersion))
|
||||
}
|
||||
|
||||
// LoadCapabilitiesJSON loads a JSON serialized capabilities structure from the reader r.
|
||||
func LoadCapabilitiesJSON(r io.Reader) (*Capabilities, error) {
|
||||
d := util.NewJSONDecoder(r)
|
||||
var c Capabilities
|
||||
return &c, d.Decode(&c)
|
||||
return v1.LoadCapabilitiesJSON(r)
|
||||
}
|
||||
|
||||
// LoadCapabilitiesVersion loads a JSON serialized capabilities structure from the specific version.
|
||||
func LoadCapabilitiesVersion(version string) (*Capabilities, error) {
|
||||
cvs, err := LoadCapabilitiesVersions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cv := range cvs {
|
||||
if cv == version {
|
||||
cont, err := caps.FS.ReadFile(cv + ".json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return LoadCapabilitiesJSON(bytes.NewReader(cont))
|
||||
}
|
||||
|
||||
}
|
||||
return nil, fmt.Errorf("no capabilities version found %v", version)
|
||||
return v1.LoadCapabilitiesVersion(version)
|
||||
}
|
||||
|
||||
// LoadCapabilitiesFile loads a JSON serialized capabilities structure from a file.
|
||||
func LoadCapabilitiesFile(file string) (*Capabilities, error) {
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
return LoadCapabilitiesJSON(fd)
|
||||
return v1.LoadCapabilitiesFile(file)
|
||||
}
|
||||
|
||||
// LoadCapabilitiesVersions loads all capabilities versions
|
||||
func LoadCapabilitiesVersions() ([]string, error) {
|
||||
ents, err := caps.FS.ReadDir(".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
capabilitiesVersions := make([]string, 0, len(ents))
|
||||
for _, ent := range ents {
|
||||
capabilitiesVersions = append(capabilitiesVersions, strings.Replace(ent.Name(), ".json", "", 1))
|
||||
}
|
||||
return capabilitiesVersions, nil
|
||||
}
|
||||
|
||||
// MinimumCompatibleVersion returns the minimum compatible OPA version based on
|
||||
// the built-ins, features, and keywords in c.
|
||||
func (c *Capabilities) MinimumCompatibleVersion() (string, bool) {
|
||||
|
||||
var maxVersion semver.Version
|
||||
|
||||
// this is the oldest OPA release that includes capabilities
|
||||
if err := maxVersion.Set("0.17.0"); err != nil {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
for _, bi := range c.Builtins {
|
||||
v, ok := minVersionIndex.Builtins[bi.Name]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
if v.Compare(maxVersion) > 0 {
|
||||
maxVersion = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, kw := range c.FutureKeywords {
|
||||
v, ok := minVersionIndex.Keywords[kw]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
if v.Compare(maxVersion) > 0 {
|
||||
maxVersion = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, feat := range c.Features {
|
||||
v, ok := minVersionIndex.Features[feat]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
if v.Compare(maxVersion) > 0 {
|
||||
maxVersion = v
|
||||
}
|
||||
}
|
||||
|
||||
return maxVersion.String(), true
|
||||
}
|
||||
|
||||
func (c *Capabilities) ContainsFeature(feature string) bool {
|
||||
for _, f := range c.Features {
|
||||
if f == feature {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// addBuiltinSorted inserts a built-in into c in sorted order. An existing built-in with the same name
|
||||
// will be overwritten.
|
||||
func (c *Capabilities) addBuiltinSorted(bi *Builtin) {
|
||||
i := sort.Search(len(c.Builtins), func(x int) bool {
|
||||
return c.Builtins[x].Name >= bi.Name
|
||||
})
|
||||
if i < len(c.Builtins) && bi.Name == c.Builtins[i].Name {
|
||||
c.Builtins[i] = bi
|
||||
return
|
||||
}
|
||||
c.Builtins = append(c.Builtins, nil)
|
||||
copy(c.Builtins[i+1:], c.Builtins[i:])
|
||||
c.Builtins[i] = bi
|
||||
return v1.LoadCapabilitiesVersions()
|
||||
}
|
||||
|
||||
1307
vendor/github.com/open-policy-agent/opa/ast/check.go
generated
vendored
1307
vendor/github.com/open-policy-agent/opa/ast/check.go
generated
vendored
File diff suppressed because it is too large
Load Diff
361
vendor/github.com/open-policy-agent/opa/ast/compare.go
generated
vendored
361
vendor/github.com/open-policy-agent/opa/ast/compare.go
generated
vendored
@@ -5,9 +5,7 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// Compare returns an integer indicating whether two AST values are less than,
|
||||
@@ -37,360 +35,5 @@ import (
|
||||
// is empty.
|
||||
// Other comparisons are consistent but not defined.
|
||||
func Compare(a, b interface{}) int {
|
||||
|
||||
if t, ok := a.(*Term); ok {
|
||||
if t == nil {
|
||||
a = nil
|
||||
} else {
|
||||
a = t.Value
|
||||
}
|
||||
}
|
||||
|
||||
if t, ok := b.(*Term); ok {
|
||||
if t == nil {
|
||||
b = nil
|
||||
} else {
|
||||
b = t.Value
|
||||
}
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
if b == nil {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
if b == nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
sortA := sortOrder(a)
|
||||
sortB := sortOrder(b)
|
||||
|
||||
if sortA < sortB {
|
||||
return -1
|
||||
} else if sortB < sortA {
|
||||
return 1
|
||||
}
|
||||
|
||||
switch a := a.(type) {
|
||||
case Null:
|
||||
return 0
|
||||
case Boolean:
|
||||
b := b.(Boolean)
|
||||
if a.Equal(b) {
|
||||
return 0
|
||||
}
|
||||
if !a {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case Number:
|
||||
if ai, err := json.Number(a).Int64(); err == nil {
|
||||
if bi, err := json.Number(b.(Number)).Int64(); err == nil {
|
||||
if ai == bi {
|
||||
return 0
|
||||
}
|
||||
if ai < bi {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// We use big.Rat for comparing big numbers.
|
||||
// It replaces big.Float due to following reason:
|
||||
// big.Float comes with a default precision of 64, and setting a
|
||||
// larger precision results in more memory being allocated
|
||||
// (regardless of the actual number we are parsing with SetString).
|
||||
//
|
||||
// Note: If we're so close to zero that big.Float says we are zero, do
|
||||
// *not* big.Rat).SetString on the original string it'll potentially
|
||||
// take very long.
|
||||
var bigA, bigB *big.Rat
|
||||
fa, ok := new(big.Float).SetString(string(a))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
if fa.IsInt() {
|
||||
if i, _ := fa.Int64(); i == 0 {
|
||||
bigA = new(big.Rat).SetInt64(0)
|
||||
}
|
||||
}
|
||||
if bigA == nil {
|
||||
bigA, ok = new(big.Rat).SetString(string(a))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
}
|
||||
|
||||
fb, ok := new(big.Float).SetString(string(b.(Number)))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
if fb.IsInt() {
|
||||
if i, _ := fb.Int64(); i == 0 {
|
||||
bigB = new(big.Rat).SetInt64(0)
|
||||
}
|
||||
}
|
||||
if bigB == nil {
|
||||
bigB, ok = new(big.Rat).SetString(string(b.(Number)))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
}
|
||||
|
||||
return bigA.Cmp(bigB)
|
||||
case String:
|
||||
b := b.(String)
|
||||
if a.Equal(b) {
|
||||
return 0
|
||||
}
|
||||
if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case Var:
|
||||
b := b.(Var)
|
||||
if a.Equal(b) {
|
||||
return 0
|
||||
}
|
||||
if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case Ref:
|
||||
b := b.(Ref)
|
||||
return termSliceCompare(a, b)
|
||||
case *Array:
|
||||
b := b.(*Array)
|
||||
return termSliceCompare(a.elems, b.elems)
|
||||
case *lazyObj:
|
||||
return Compare(a.force(), b)
|
||||
case *object:
|
||||
if x, ok := b.(*lazyObj); ok {
|
||||
b = x.force()
|
||||
}
|
||||
b := b.(*object)
|
||||
return a.Compare(b)
|
||||
case Set:
|
||||
b := b.(Set)
|
||||
return a.Compare(b)
|
||||
case *ArrayComprehension:
|
||||
b := b.(*ArrayComprehension)
|
||||
if cmp := Compare(a.Term, b.Term); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return Compare(a.Body, b.Body)
|
||||
case *ObjectComprehension:
|
||||
b := b.(*ObjectComprehension)
|
||||
if cmp := Compare(a.Key, b.Key); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
if cmp := Compare(a.Value, b.Value); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return Compare(a.Body, b.Body)
|
||||
case *SetComprehension:
|
||||
b := b.(*SetComprehension)
|
||||
if cmp := Compare(a.Term, b.Term); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return Compare(a.Body, b.Body)
|
||||
case Call:
|
||||
b := b.(Call)
|
||||
return termSliceCompare(a, b)
|
||||
case *Expr:
|
||||
b := b.(*Expr)
|
||||
return a.Compare(b)
|
||||
case *SomeDecl:
|
||||
b := b.(*SomeDecl)
|
||||
return a.Compare(b)
|
||||
case *Every:
|
||||
b := b.(*Every)
|
||||
return a.Compare(b)
|
||||
case *With:
|
||||
b := b.(*With)
|
||||
return a.Compare(b)
|
||||
case Body:
|
||||
b := b.(Body)
|
||||
return a.Compare(b)
|
||||
case *Head:
|
||||
b := b.(*Head)
|
||||
return a.Compare(b)
|
||||
case *Rule:
|
||||
b := b.(*Rule)
|
||||
return a.Compare(b)
|
||||
case Args:
|
||||
b := b.(Args)
|
||||
return termSliceCompare(a, b)
|
||||
case *Import:
|
||||
b := b.(*Import)
|
||||
return a.Compare(b)
|
||||
case *Package:
|
||||
b := b.(*Package)
|
||||
return a.Compare(b)
|
||||
case *Annotations:
|
||||
b := b.(*Annotations)
|
||||
return a.Compare(b)
|
||||
case *Module:
|
||||
b := b.(*Module)
|
||||
return a.Compare(b)
|
||||
}
|
||||
panic(fmt.Sprintf("illegal value: %T", a))
|
||||
}
|
||||
|
||||
type termSlice []*Term
|
||||
|
||||
func (s termSlice) Less(i, j int) bool { return Compare(s[i].Value, s[j].Value) < 0 }
|
||||
func (s termSlice) Swap(i, j int) { x := s[i]; s[i] = s[j]; s[j] = x }
|
||||
func (s termSlice) Len() int { return len(s) }
|
||||
|
||||
func sortOrder(x interface{}) int {
|
||||
switch x.(type) {
|
||||
case Null:
|
||||
return 0
|
||||
case Boolean:
|
||||
return 1
|
||||
case Number:
|
||||
return 2
|
||||
case String:
|
||||
return 3
|
||||
case Var:
|
||||
return 4
|
||||
case Ref:
|
||||
return 5
|
||||
case *Array:
|
||||
return 6
|
||||
case Object:
|
||||
return 7
|
||||
case Set:
|
||||
return 8
|
||||
case *ArrayComprehension:
|
||||
return 9
|
||||
case *ObjectComprehension:
|
||||
return 10
|
||||
case *SetComprehension:
|
||||
return 11
|
||||
case Call:
|
||||
return 12
|
||||
case Args:
|
||||
return 13
|
||||
case *Expr:
|
||||
return 100
|
||||
case *SomeDecl:
|
||||
return 101
|
||||
case *Every:
|
||||
return 102
|
||||
case *With:
|
||||
return 110
|
||||
case *Head:
|
||||
return 120
|
||||
case Body:
|
||||
return 200
|
||||
case *Rule:
|
||||
return 1000
|
||||
case *Import:
|
||||
return 1001
|
||||
case *Package:
|
||||
return 1002
|
||||
case *Annotations:
|
||||
return 1003
|
||||
case *Module:
|
||||
return 10000
|
||||
}
|
||||
panic(fmt.Sprintf("illegal value: %T", x))
|
||||
}
|
||||
|
||||
func importsCompare(a, b []*Import) int {
|
||||
minLen := len(a)
|
||||
if len(b) < minLen {
|
||||
minLen = len(b)
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func annotationsCompare(a, b []*Annotations) int {
|
||||
minLen := len(a)
|
||||
if len(b) < minLen {
|
||||
minLen = len(b)
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func rulesCompare(a, b []*Rule) int {
|
||||
minLen := len(a)
|
||||
if len(b) < minLen {
|
||||
minLen = len(b)
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
if cmp := a[i].Compare(b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
}
|
||||
if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func termSliceCompare(a, b []*Term) int {
|
||||
minLen := len(a)
|
||||
if len(b) < minLen {
|
||||
minLen = len(b)
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
if cmp := Compare(a[i], b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
} else if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func withSliceCompare(a, b []*With) int {
|
||||
minLen := len(a)
|
||||
if len(b) < minLen {
|
||||
minLen = len(b)
|
||||
}
|
||||
for i := 0; i < minLen; i++ {
|
||||
if cmp := Compare(a[i], b[i]); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if len(a) < len(b) {
|
||||
return -1
|
||||
} else if len(b) < len(a) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
return v1.Compare(a, b)
|
||||
}
|
||||
|
||||
5811
vendor/github.com/open-policy-agent/opa/ast/compile.go
generated
vendored
5811
vendor/github.com/open-policy-agent/opa/ast/compile.go
generated
vendored
File diff suppressed because it is too large
Load Diff
34
vendor/github.com/open-policy-agent/opa/ast/compilehelper.go
generated
vendored
34
vendor/github.com/open-policy-agent/opa/ast/compilehelper.go
generated
vendored
@@ -4,41 +4,29 @@
|
||||
|
||||
package ast
|
||||
|
||||
import v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
|
||||
// CompileModules takes a set of Rego modules represented as strings and
|
||||
// compiles them for evaluation. The keys of the map are used as filenames.
|
||||
func CompileModules(modules map[string]string) (*Compiler, error) {
|
||||
return CompileModulesWithOpt(modules, CompileOpts{})
|
||||
return CompileModulesWithOpt(modules, CompileOpts{
|
||||
ParserOptions: ParserOptions{
|
||||
RegoVersion: DefaultRegoVersion,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CompileOpts defines a set of options for the compiler.
|
||||
type CompileOpts struct {
|
||||
EnablePrintStatements bool
|
||||
ParserOptions ParserOptions
|
||||
}
|
||||
type CompileOpts = v1.CompileOpts
|
||||
|
||||
// CompileModulesWithOpt takes a set of Rego modules represented as strings and
|
||||
// compiles them for evaluation. The keys of the map are used as filenames.
|
||||
func CompileModulesWithOpt(modules map[string]string, opts CompileOpts) (*Compiler, error) {
|
||||
|
||||
parsed := make(map[string]*Module, len(modules))
|
||||
|
||||
for f, module := range modules {
|
||||
var pm *Module
|
||||
var err error
|
||||
if pm, err = ParseModuleWithOpts(f, module, opts.ParserOptions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed[f] = pm
|
||||
if opts.ParserOptions.RegoVersion == RegoUndefined {
|
||||
opts.ParserOptions.RegoVersion = DefaultRegoVersion
|
||||
}
|
||||
|
||||
compiler := NewCompiler().WithEnablePrintStatements(opts.EnablePrintStatements)
|
||||
compiler.Compile(parsed)
|
||||
|
||||
if compiler.Failed() {
|
||||
return nil, compiler.Errors
|
||||
}
|
||||
|
||||
return compiler, nil
|
||||
return v1.CompileModulesWithOpt(modules, opts)
|
||||
}
|
||||
|
||||
// MustCompileModules compiles a set of Rego modules represented as strings. If
|
||||
|
||||
9
vendor/github.com/open-policy-agent/opa/ast/compilemetrics.go
generated
vendored
9
vendor/github.com/open-policy-agent/opa/ast/compilemetrics.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// Copyright 2020 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
const (
|
||||
compileStageComprehensionIndexBuild = "compile_stage_comprehension_index_build"
|
||||
)
|
||||
42
vendor/github.com/open-policy-agent/opa/ast/conflicts.go
generated
vendored
42
vendor/github.com/open-policy-agent/opa/ast/conflicts.go
generated
vendored
@@ -5,49 +5,11 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"strings"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// CheckPathConflicts returns a set of errors indicating paths that
|
||||
// are in conflict with the result of the provided callable.
|
||||
func CheckPathConflicts(c *Compiler, exists func([]string) (bool, error)) Errors {
|
||||
var errs Errors
|
||||
|
||||
root := c.RuleTree.Child(DefaultRootDocument.Value)
|
||||
if root == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, node := range root.Children {
|
||||
errs = append(errs, checkDocumentConflicts(node, exists, nil)...)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func checkDocumentConflicts(node *TreeNode, exists func([]string) (bool, error), path []string) Errors {
|
||||
|
||||
switch key := node.Key.(type) {
|
||||
case String:
|
||||
path = append(path, string(key))
|
||||
default: // other key types cannot conflict with data
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(node.Values) > 0 {
|
||||
s := strings.Join(path, "/")
|
||||
if ok, err := exists(path); err != nil {
|
||||
return Errors{NewError(CompileErr, node.Values[0].(*Rule).Loc(), "conflict check for data path %v: %v", s, err.Error())}
|
||||
} else if ok {
|
||||
return Errors{NewError(CompileErr, node.Values[0].(*Rule).Loc(), "conflicting rule for data path %v found", s)}
|
||||
}
|
||||
}
|
||||
|
||||
var errs Errors
|
||||
|
||||
for _, child := range node.Children {
|
||||
errs = append(errs, checkDocumentConflicts(child, exists, path)...)
|
||||
}
|
||||
|
||||
return errs
|
||||
return v1.CheckPathConflicts(c, exists)
|
||||
}
|
||||
|
||||
36
vendor/github.com/open-policy-agent/opa/ast/doc.go
generated
vendored
36
vendor/github.com/open-policy-agent/opa/ast/doc.go
generated
vendored
@@ -1,36 +1,8 @@
|
||||
// Copyright 2016 The OPA Authors. All rights reserved.
|
||||
// Copyright 2024 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ast declares Rego syntax tree types and also includes a parser and compiler for preparing policies for execution in the policy engine.
|
||||
//
|
||||
// Rego policies are defined using a relatively small set of types: modules, package and import declarations, rules, expressions, and terms. At their core, policies consist of rules that are defined by one or more expressions over documents available to the policy engine. The expressions are defined by intrinsic values (terms) such as strings, objects, variables, etc.
|
||||
//
|
||||
// Rego policies are typically defined in text files and then parsed and compiled by the policy engine at runtime. The parsing stage takes the text or string representation of the policy and converts it into an abstract syntax tree (AST) that consists of the types mentioned above. The AST is organized as follows:
|
||||
//
|
||||
// Module
|
||||
// |
|
||||
// +--- Package (Reference)
|
||||
// |
|
||||
// +--- Imports
|
||||
// | |
|
||||
// | +--- Import (Term)
|
||||
// |
|
||||
// +--- Rules
|
||||
// |
|
||||
// +--- Rule
|
||||
// |
|
||||
// +--- Head
|
||||
// | |
|
||||
// | +--- Name (Variable)
|
||||
// | |
|
||||
// | +--- Key (Term)
|
||||
// | |
|
||||
// | +--- Value (Term)
|
||||
// |
|
||||
// +--- Body
|
||||
// |
|
||||
// +--- Expression (Term | Terms | Variable Declaration)
|
||||
//
|
||||
// At query time, the policy engine expects policies to have been compiled. The compilation stage takes one or more modules and compiles them into a format that the policy engine supports.
|
||||
// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended.
|
||||
// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead.
|
||||
// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information.
|
||||
package ast
|
||||
|
||||
518
vendor/github.com/open-policy-agent/opa/ast/env.go
generated
vendored
518
vendor/github.com/open-policy-agent/opa/ast/env.go
generated
vendored
@@ -5,522 +5,8 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/open-policy-agent/opa/types"
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// TypeEnv contains type info for static analysis such as type checking.
|
||||
type TypeEnv struct {
|
||||
tree *typeTreeNode
|
||||
next *TypeEnv
|
||||
newChecker func() *typeChecker
|
||||
}
|
||||
|
||||
// newTypeEnv returns an empty TypeEnv. The constructor is not exported because
|
||||
// type environments should only be created by the type checker.
|
||||
func newTypeEnv(f func() *typeChecker) *TypeEnv {
|
||||
return &TypeEnv{
|
||||
tree: newTypeTree(),
|
||||
newChecker: f,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the type of x.
|
||||
func (env *TypeEnv) Get(x interface{}) types.Type {
|
||||
|
||||
if term, ok := x.(*Term); ok {
|
||||
x = term.Value
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
|
||||
// Scalars.
|
||||
case Null:
|
||||
return types.NewNull()
|
||||
case Boolean:
|
||||
return types.NewBoolean()
|
||||
case Number:
|
||||
return types.NewNumber()
|
||||
case String:
|
||||
return types.NewString()
|
||||
|
||||
// Composites.
|
||||
case *Array:
|
||||
static := make([]types.Type, x.Len())
|
||||
for i := range static {
|
||||
tpe := env.Get(x.Elem(i).Value)
|
||||
static[i] = tpe
|
||||
}
|
||||
|
||||
var dynamic types.Type
|
||||
if len(static) == 0 {
|
||||
dynamic = types.A
|
||||
}
|
||||
|
||||
return types.NewArray(static, dynamic)
|
||||
|
||||
case *lazyObj:
|
||||
return env.Get(x.force())
|
||||
case *object:
|
||||
static := []*types.StaticProperty{}
|
||||
var dynamic *types.DynamicProperty
|
||||
|
||||
x.Foreach(func(k, v *Term) {
|
||||
if IsConstant(k.Value) {
|
||||
kjson, err := JSON(k.Value)
|
||||
if err == nil {
|
||||
tpe := env.Get(v)
|
||||
static = append(static, types.NewStaticProperty(kjson, tpe))
|
||||
return
|
||||
}
|
||||
}
|
||||
// Can't handle it as a static property, fallback to dynamic
|
||||
typeK := env.Get(k.Value)
|
||||
typeV := env.Get(v.Value)
|
||||
dynamic = types.NewDynamicProperty(typeK, typeV)
|
||||
})
|
||||
|
||||
if len(static) == 0 && dynamic == nil {
|
||||
dynamic = types.NewDynamicProperty(types.A, types.A)
|
||||
}
|
||||
|
||||
return types.NewObject(static, dynamic)
|
||||
|
||||
case Set:
|
||||
var tpe types.Type
|
||||
x.Foreach(func(elem *Term) {
|
||||
other := env.Get(elem.Value)
|
||||
tpe = types.Or(tpe, other)
|
||||
})
|
||||
if tpe == nil {
|
||||
tpe = types.A
|
||||
}
|
||||
return types.NewSet(tpe)
|
||||
|
||||
// Comprehensions.
|
||||
case *ArrayComprehension:
|
||||
cpy, errs := env.newChecker().CheckBody(env, x.Body)
|
||||
if len(errs) == 0 {
|
||||
return types.NewArray(nil, cpy.Get(x.Term))
|
||||
}
|
||||
return nil
|
||||
case *ObjectComprehension:
|
||||
cpy, errs := env.newChecker().CheckBody(env, x.Body)
|
||||
if len(errs) == 0 {
|
||||
return types.NewObject(nil, types.NewDynamicProperty(cpy.Get(x.Key), cpy.Get(x.Value)))
|
||||
}
|
||||
return nil
|
||||
case *SetComprehension:
|
||||
cpy, errs := env.newChecker().CheckBody(env, x.Body)
|
||||
if len(errs) == 0 {
|
||||
return types.NewSet(cpy.Get(x.Term))
|
||||
}
|
||||
return nil
|
||||
|
||||
// Refs.
|
||||
case Ref:
|
||||
return env.getRef(x)
|
||||
|
||||
// Vars.
|
||||
case Var:
|
||||
if node := env.tree.Child(x); node != nil {
|
||||
return node.Value()
|
||||
}
|
||||
if env.next != nil {
|
||||
return env.next.Get(x)
|
||||
}
|
||||
return nil
|
||||
|
||||
// Calls.
|
||||
case Call:
|
||||
return nil
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func (env *TypeEnv) getRef(ref Ref) types.Type {
|
||||
|
||||
node := env.tree.Child(ref[0].Value)
|
||||
if node == nil {
|
||||
return env.getRefFallback(ref)
|
||||
}
|
||||
|
||||
return env.getRefRec(node, ref, ref[1:])
|
||||
}
|
||||
|
||||
func (env *TypeEnv) getRefFallback(ref Ref) types.Type {
|
||||
|
||||
if env.next != nil {
|
||||
return env.next.Get(ref)
|
||||
}
|
||||
|
||||
if RootDocumentNames.Contains(ref[0]) {
|
||||
return types.A
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *TypeEnv) getRefRec(node *typeTreeNode, ref, tail Ref) types.Type {
|
||||
if len(tail) == 0 {
|
||||
return env.getRefRecExtent(node)
|
||||
}
|
||||
|
||||
if node.Leaf() {
|
||||
if node.children.Len() > 0 {
|
||||
if child := node.Child(tail[0].Value); child != nil {
|
||||
return env.getRefRec(child, ref, tail[1:])
|
||||
}
|
||||
}
|
||||
return selectRef(node.Value(), tail)
|
||||
}
|
||||
|
||||
if !IsConstant(tail[0].Value) {
|
||||
return selectRef(env.getRefRecExtent(node), tail)
|
||||
}
|
||||
|
||||
child := node.Child(tail[0].Value)
|
||||
if child == nil {
|
||||
return env.getRefFallback(ref)
|
||||
}
|
||||
|
||||
return env.getRefRec(child, ref, tail[1:])
|
||||
}
|
||||
|
||||
func (env *TypeEnv) getRefRecExtent(node *typeTreeNode) types.Type {
|
||||
|
||||
if node.Leaf() {
|
||||
return node.Value()
|
||||
}
|
||||
|
||||
children := []*types.StaticProperty{}
|
||||
|
||||
node.Children().Iter(func(k, v util.T) bool {
|
||||
key := k.(Value)
|
||||
child := v.(*typeTreeNode)
|
||||
|
||||
tpe := env.getRefRecExtent(child)
|
||||
|
||||
// NOTE(sr): Converting to Golang-native types here is an extension of what we did
|
||||
// before -- only supporting strings. But since we cannot differentiate sets and arrays
|
||||
// that way, we could reconsider.
|
||||
switch key.(type) {
|
||||
case String, Number, Boolean: // skip anything else
|
||||
propKey, err := JSON(key)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("unreachable, ValueToInterface: %w", err))
|
||||
}
|
||||
children = append(children, types.NewStaticProperty(propKey, tpe))
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// TODO(tsandall): for now, these objects can have any dynamic properties
|
||||
// because we don't have schema for base docs. Once schemas are supported
|
||||
// we can improve this.
|
||||
return types.NewObject(children, types.NewDynamicProperty(types.S, types.A))
|
||||
}
|
||||
|
||||
func (env *TypeEnv) wrap() *TypeEnv {
|
||||
cpy := *env
|
||||
cpy.next = env
|
||||
cpy.tree = newTypeTree()
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// typeTreeNode is used to store type information in a tree.
|
||||
type typeTreeNode struct {
|
||||
key Value
|
||||
value types.Type
|
||||
children *util.HashMap
|
||||
}
|
||||
|
||||
func newTypeTree() *typeTreeNode {
|
||||
return &typeTreeNode{
|
||||
key: nil,
|
||||
value: nil,
|
||||
children: util.NewHashMap(valueEq, valueHash),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Child(key Value) *typeTreeNode {
|
||||
value, ok := n.children.Get(key)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return value.(*typeTreeNode)
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Children() *util.HashMap {
|
||||
return n.children
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Get(path Ref) types.Type {
|
||||
curr := n
|
||||
for _, term := range path {
|
||||
child, ok := curr.children.Get(term.Value)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
curr = child.(*typeTreeNode)
|
||||
}
|
||||
return curr.Value()
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Leaf() bool {
|
||||
return n.value != nil
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) PutOne(key Value, tpe types.Type) {
|
||||
c, ok := n.children.Get(key)
|
||||
|
||||
var child *typeTreeNode
|
||||
if !ok {
|
||||
child = newTypeTree()
|
||||
child.key = key
|
||||
n.children.Put(key, child)
|
||||
} else {
|
||||
child = c.(*typeTreeNode)
|
||||
}
|
||||
|
||||
child.value = tpe
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Put(path Ref, tpe types.Type) {
|
||||
curr := n
|
||||
for _, term := range path {
|
||||
c, ok := curr.children.Get(term.Value)
|
||||
|
||||
var child *typeTreeNode
|
||||
if !ok {
|
||||
child = newTypeTree()
|
||||
child.key = term.Value
|
||||
curr.children.Put(child.key, child)
|
||||
} else {
|
||||
child = c.(*typeTreeNode)
|
||||
}
|
||||
|
||||
curr = child
|
||||
}
|
||||
curr.value = tpe
|
||||
}
|
||||
|
||||
// Insert inserts tpe at path in the tree, but also merges the value into any types.Object present along that path.
|
||||
// If a types.Object is inserted, any leafs already present further down the tree are merged into the inserted object.
|
||||
// path must be ground.
|
||||
func (n *typeTreeNode) Insert(path Ref, tpe types.Type, env *TypeEnv) {
|
||||
curr := n
|
||||
for i, term := range path {
|
||||
c, ok := curr.children.Get(term.Value)
|
||||
|
||||
var child *typeTreeNode
|
||||
if !ok {
|
||||
child = newTypeTree()
|
||||
child.key = term.Value
|
||||
curr.children.Put(child.key, child)
|
||||
} else {
|
||||
child = c.(*typeTreeNode)
|
||||
|
||||
if child.value != nil && i+1 < len(path) {
|
||||
// If child has an object value, merge the new value into it.
|
||||
if o, ok := child.value.(*types.Object); ok {
|
||||
var err error
|
||||
child.value, err = insertIntoObject(o, path[i+1:], tpe, env)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("unreachable, insertIntoObject: %w", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curr = child
|
||||
}
|
||||
|
||||
curr.value = mergeTypes(curr.value, tpe)
|
||||
|
||||
if _, ok := tpe.(*types.Object); ok && curr.children.Len() > 0 {
|
||||
// merge all leafs into the inserted object
|
||||
leafs := curr.Leafs()
|
||||
for p, t := range leafs {
|
||||
var err error
|
||||
curr.value, err = insertIntoObject(curr.value.(*types.Object), *p, t, env)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("unreachable, insertIntoObject: %w", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mergeTypes merges the types of 'a' and 'b'. If both are sets, their 'of' types are joined with an types.Or.
|
||||
// If both are objects, the key types of their dynamic properties are joined with types.Or:s, and their value types
|
||||
// are recursively merged (using mergeTypes).
|
||||
// If 'a' and 'b' are both objects, and at least one of them have static properties, they are joined
|
||||
// with an types.Or, instead of being merged.
|
||||
// If 'a' is an Any containing an Object, and 'b' is an Object (or vice versa); AND both objects have no
|
||||
// static properties, they are merged.
|
||||
// If 'a' and 'b' are different types, they are joined with an types.Or.
|
||||
func mergeTypes(a, b types.Type) types.Type {
|
||||
if a == nil {
|
||||
return b
|
||||
}
|
||||
|
||||
if b == nil {
|
||||
return a
|
||||
}
|
||||
|
||||
switch a := a.(type) {
|
||||
case *types.Object:
|
||||
if bObj, ok := b.(*types.Object); ok && len(a.StaticProperties()) == 0 && len(bObj.StaticProperties()) == 0 {
|
||||
if len(a.StaticProperties()) > 0 || len(bObj.StaticProperties()) > 0 {
|
||||
return types.Or(a, bObj)
|
||||
}
|
||||
|
||||
aDynProps := a.DynamicProperties()
|
||||
bDynProps := bObj.DynamicProperties()
|
||||
dynProps := types.NewDynamicProperty(
|
||||
types.Or(aDynProps.Key, bDynProps.Key),
|
||||
mergeTypes(aDynProps.Value, bDynProps.Value))
|
||||
return types.NewObject(nil, dynProps)
|
||||
} else if bAny, ok := b.(types.Any); ok && len(a.StaticProperties()) == 0 {
|
||||
// If a is an object type with no static components ...
|
||||
for _, t := range bAny {
|
||||
if tObj, ok := t.(*types.Object); ok && len(tObj.StaticProperties()) == 0 {
|
||||
// ... and b is a types.Any containing an object with no static components, we merge them.
|
||||
aDynProps := a.DynamicProperties()
|
||||
tDynProps := tObj.DynamicProperties()
|
||||
tDynProps.Key = types.Or(tDynProps.Key, aDynProps.Key)
|
||||
tDynProps.Value = types.Or(tDynProps.Value, aDynProps.Value)
|
||||
return bAny
|
||||
}
|
||||
}
|
||||
}
|
||||
case *types.Set:
|
||||
if bSet, ok := b.(*types.Set); ok {
|
||||
return types.NewSet(types.Or(a.Of(), bSet.Of()))
|
||||
}
|
||||
case types.Any:
|
||||
if _, ok := b.(types.Any); !ok {
|
||||
return mergeTypes(b, a)
|
||||
}
|
||||
}
|
||||
|
||||
return types.Or(a, b)
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) String() string {
|
||||
b := strings.Builder{}
|
||||
|
||||
if k := n.key; k != nil {
|
||||
b.WriteString(k.String())
|
||||
} else {
|
||||
b.WriteString("-")
|
||||
}
|
||||
|
||||
if v := n.value; v != nil {
|
||||
b.WriteString(": ")
|
||||
b.WriteString(v.String())
|
||||
}
|
||||
|
||||
n.children.Iter(func(_, v util.T) bool {
|
||||
if child, ok := v.(*typeTreeNode); ok {
|
||||
b.WriteString("\n\t+ ")
|
||||
s := child.String()
|
||||
s = strings.ReplaceAll(s, "\n", "\n\t")
|
||||
b.WriteString(s)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func insertIntoObject(o *types.Object, path Ref, tpe types.Type, env *TypeEnv) (*types.Object, error) {
|
||||
if len(path) == 0 {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
key := env.Get(path[0].Value)
|
||||
|
||||
if len(path) == 1 {
|
||||
var dynamicProps *types.DynamicProperty
|
||||
if dp := o.DynamicProperties(); dp != nil {
|
||||
dynamicProps = types.NewDynamicProperty(types.Or(o.DynamicProperties().Key, key), types.Or(o.DynamicProperties().Value, tpe))
|
||||
} else {
|
||||
dynamicProps = types.NewDynamicProperty(key, tpe)
|
||||
}
|
||||
return types.NewObject(o.StaticProperties(), dynamicProps), nil
|
||||
}
|
||||
|
||||
child, err := insertIntoObject(types.NewObject(nil, nil), path[1:], tpe, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dynamicProps *types.DynamicProperty
|
||||
if dp := o.DynamicProperties(); dp != nil {
|
||||
dynamicProps = types.NewDynamicProperty(types.Or(o.DynamicProperties().Key, key), types.Or(o.DynamicProperties().Value, child))
|
||||
} else {
|
||||
dynamicProps = types.NewDynamicProperty(key, child)
|
||||
}
|
||||
return types.NewObject(o.StaticProperties(), dynamicProps), nil
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Leafs() map[*Ref]types.Type {
|
||||
leafs := map[*Ref]types.Type{}
|
||||
n.children.Iter(func(_, v util.T) bool {
|
||||
collectLeafs(v.(*typeTreeNode), nil, leafs)
|
||||
return false
|
||||
})
|
||||
return leafs
|
||||
}
|
||||
|
||||
func collectLeafs(n *typeTreeNode, path Ref, leafs map[*Ref]types.Type) {
|
||||
nPath := append(path, NewTerm(n.key))
|
||||
if n.Leaf() {
|
||||
leafs[&nPath] = n.Value()
|
||||
return
|
||||
}
|
||||
n.children.Iter(func(_, v util.T) bool {
|
||||
collectLeafs(v.(*typeTreeNode), nPath, leafs)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Value() types.Type {
|
||||
return n.value
|
||||
}
|
||||
|
||||
// selectConstant returns the attribute of the type referred to by the term. If
|
||||
// the attribute type cannot be determined, nil is returned.
|
||||
func selectConstant(tpe types.Type, term *Term) types.Type {
|
||||
x, err := JSON(term.Value)
|
||||
if err == nil {
|
||||
return types.Select(tpe, x)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// selectRef returns the type of the nested attribute referred to by ref. If
|
||||
// the attribute type cannot be determined, nil is returned. If the ref
|
||||
// contains vars or refs, then the returned type will be a union of the
|
||||
// possible types.
|
||||
func selectRef(tpe types.Type, ref Ref) types.Type {
|
||||
|
||||
if tpe == nil || len(ref) == 0 {
|
||||
return tpe
|
||||
}
|
||||
|
||||
head, tail := ref[0], ref[1:]
|
||||
|
||||
switch head.Value.(type) {
|
||||
case Var, Ref, *Array, Object, Set:
|
||||
return selectRef(types.Values(tpe), tail)
|
||||
default:
|
||||
return selectRef(selectConstant(tpe, head), tail)
|
||||
}
|
||||
}
|
||||
type TypeEnv = v1.TypeEnv
|
||||
|
||||
99
vendor/github.com/open-policy-agent/opa/ast/errors.go
generated
vendored
99
vendor/github.com/open-policy-agent/opa/ast/errors.go
generated
vendored
@@ -5,119 +5,42 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// Errors represents a series of errors encountered during parsing, compiling,
|
||||
// etc.
|
||||
type Errors []*Error
|
||||
|
||||
func (e Errors) Error() string {
|
||||
|
||||
if len(e) == 0 {
|
||||
return "no error(s)"
|
||||
}
|
||||
|
||||
if len(e) == 1 {
|
||||
return fmt.Sprintf("1 error occurred: %v", e[0].Error())
|
||||
}
|
||||
|
||||
s := make([]string, len(e))
|
||||
for i, err := range e {
|
||||
s[i] = err.Error()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d errors occurred:\n%s", len(e), strings.Join(s, "\n"))
|
||||
}
|
||||
|
||||
// Sort sorts the error slice by location. If the locations are equal then the
|
||||
// error message is compared.
|
||||
func (e Errors) Sort() {
|
||||
sort.Slice(e, func(i, j int) bool {
|
||||
a := e[i]
|
||||
b := e[j]
|
||||
|
||||
if cmp := a.Location.Compare(b.Location); cmp != 0 {
|
||||
return cmp < 0
|
||||
}
|
||||
|
||||
return a.Error() < b.Error()
|
||||
})
|
||||
}
|
||||
type Errors = v1.Errors
|
||||
|
||||
const (
|
||||
// ParseErr indicates an unclassified parse error occurred.
|
||||
ParseErr = "rego_parse_error"
|
||||
ParseErr = v1.ParseErr
|
||||
|
||||
// CompileErr indicates an unclassified compile error occurred.
|
||||
CompileErr = "rego_compile_error"
|
||||
CompileErr = v1.CompileErr
|
||||
|
||||
// TypeErr indicates a type error was caught.
|
||||
TypeErr = "rego_type_error"
|
||||
TypeErr = v1.TypeErr
|
||||
|
||||
// UnsafeVarErr indicates an unsafe variable was found during compilation.
|
||||
UnsafeVarErr = "rego_unsafe_var_error"
|
||||
UnsafeVarErr = v1.UnsafeVarErr
|
||||
|
||||
// RecursionErr indicates recursion was found during compilation.
|
||||
RecursionErr = "rego_recursion_error"
|
||||
RecursionErr = v1.RecursionErr
|
||||
)
|
||||
|
||||
// IsError returns true if err is an AST error with code.
|
||||
func IsError(code string, err error) bool {
|
||||
if err, ok := err.(*Error); ok {
|
||||
return err.Code == code
|
||||
}
|
||||
return false
|
||||
return v1.IsError(code, err)
|
||||
}
|
||||
|
||||
// ErrorDetails defines the interface for detailed error messages.
|
||||
type ErrorDetails interface {
|
||||
Lines() []string
|
||||
}
|
||||
type ErrorDetails = v1.ErrorDetails
|
||||
|
||||
// Error represents a single error caught during parsing, compiling, etc.
|
||||
type Error struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Location *Location `json:"location,omitempty"`
|
||||
Details ErrorDetails `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
|
||||
var prefix string
|
||||
|
||||
if e.Location != nil {
|
||||
|
||||
if len(e.Location.File) > 0 {
|
||||
prefix += e.Location.File + ":" + fmt.Sprint(e.Location.Row)
|
||||
} else {
|
||||
prefix += fmt.Sprint(e.Location.Row) + ":" + fmt.Sprint(e.Location.Col)
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("%v: %v", e.Code, e.Message)
|
||||
|
||||
if len(prefix) > 0 {
|
||||
msg = prefix + ": " + msg
|
||||
}
|
||||
|
||||
if e.Details != nil {
|
||||
for _, line := range e.Details.Lines() {
|
||||
msg += "\n\t" + line
|
||||
}
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
type Error = v1.Error
|
||||
|
||||
// NewError returns a new Error object.
|
||||
func NewError(code string, loc *Location, f string, a ...interface{}) *Error {
|
||||
return &Error{
|
||||
Code: code,
|
||||
Location: loc,
|
||||
Message: fmt.Sprintf(f, a...),
|
||||
}
|
||||
return v1.NewError(code, loc, f, a...)
|
||||
}
|
||||
|
||||
896
vendor/github.com/open-policy-agent/opa/ast/index.go
generated
vendored
896
vendor/github.com/open-policy-agent/opa/ast/index.go
generated
vendored
@@ -5,904 +5,16 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// RuleIndex defines the interface for rule indices.
|
||||
type RuleIndex interface {
|
||||
|
||||
// Build tries to construct an index for the given rules. If the index was
|
||||
// constructed, it returns true, otherwise false.
|
||||
Build(rules []*Rule) bool
|
||||
|
||||
// Lookup searches the index for rules that will match the provided
|
||||
// resolver. If the resolver returns an error, it is returned via err.
|
||||
Lookup(resolver ValueResolver) (*IndexResult, error)
|
||||
|
||||
// AllRules traverses the index and returns all rules that will match
|
||||
// the provided resolver without any optimizations (effectively with
|
||||
// indexing disabled). If the resolver returns an error, it is returned
|
||||
// via err.
|
||||
AllRules(resolver ValueResolver) (*IndexResult, error)
|
||||
}
|
||||
type RuleIndex v1.RuleIndex
|
||||
|
||||
// IndexResult contains the result of an index lookup.
|
||||
type IndexResult struct {
|
||||
Kind RuleKind
|
||||
Rules []*Rule
|
||||
Else map[*Rule][]*Rule
|
||||
Default *Rule
|
||||
EarlyExit bool
|
||||
OnlyGroundRefs bool
|
||||
}
|
||||
type IndexResult = v1.IndexResult
|
||||
|
||||
// NewIndexResult returns a new IndexResult object.
|
||||
func NewIndexResult(kind RuleKind) *IndexResult {
|
||||
return &IndexResult{
|
||||
Kind: kind,
|
||||
Else: map[*Rule][]*Rule{},
|
||||
}
|
||||
}
|
||||
|
||||
// Empty returns true if there are no rules to evaluate.
|
||||
func (ir *IndexResult) Empty() bool {
|
||||
return len(ir.Rules) == 0 && ir.Default == nil
|
||||
}
|
||||
|
||||
type baseDocEqIndex struct {
|
||||
skipIndexing Set
|
||||
isVirtual func(Ref) bool
|
||||
root *trieNode
|
||||
defaultRule *Rule
|
||||
kind RuleKind
|
||||
onlyGroundRefs bool
|
||||
}
|
||||
|
||||
func newBaseDocEqIndex(isVirtual func(Ref) bool) *baseDocEqIndex {
|
||||
return &baseDocEqIndex{
|
||||
skipIndexing: NewSet(NewTerm(InternalPrint.Ref())),
|
||||
isVirtual: isVirtual,
|
||||
root: newTrieNodeImpl(),
|
||||
onlyGroundRefs: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *baseDocEqIndex) Build(rules []*Rule) bool {
|
||||
if len(rules) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
i.kind = rules[0].Head.RuleKind()
|
||||
indices := newrefindices(i.isVirtual)
|
||||
|
||||
// build indices for each rule.
|
||||
for idx := range rules {
|
||||
WalkRules(rules[idx], func(rule *Rule) bool {
|
||||
if rule.Default {
|
||||
i.defaultRule = rule
|
||||
return false
|
||||
}
|
||||
if i.onlyGroundRefs {
|
||||
i.onlyGroundRefs = rule.Head.Reference.IsGround()
|
||||
}
|
||||
var skip bool
|
||||
for _, expr := range rule.Body {
|
||||
if op := expr.OperatorTerm(); op != nil && i.skipIndexing.Contains(op) {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !skip {
|
||||
for _, expr := range rule.Body {
|
||||
indices.Update(rule, expr)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// build trie out of indices.
|
||||
for idx := range rules {
|
||||
var prio int
|
||||
WalkRules(rules[idx], func(rule *Rule) bool {
|
||||
if rule.Default {
|
||||
return false
|
||||
}
|
||||
node := i.root
|
||||
if indices.Indexed(rule) {
|
||||
for _, ref := range indices.Sorted() {
|
||||
node = node.Insert(ref, indices.Value(rule, ref), indices.Mapper(rule, ref))
|
||||
}
|
||||
}
|
||||
// Insert rule into trie with (insertion order, priority order)
|
||||
// tuple. Retaining the insertion order allows us to return rules
|
||||
// in the order they were passed to this function.
|
||||
node.append([...]int{idx, prio}, rule)
|
||||
prio++
|
||||
return false
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) {
|
||||
|
||||
tr := newTrieTraversalResult()
|
||||
|
||||
err := i.root.Traverse(resolver, tr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := NewIndexResult(i.kind)
|
||||
result.Default = i.defaultRule
|
||||
result.OnlyGroundRefs = i.onlyGroundRefs
|
||||
result.Rules = make([]*Rule, 0, len(tr.ordering))
|
||||
|
||||
for _, pos := range tr.ordering {
|
||||
sort.Slice(tr.unordered[pos], func(i, j int) bool {
|
||||
return tr.unordered[pos][i].prio[1] < tr.unordered[pos][j].prio[1]
|
||||
})
|
||||
nodes := tr.unordered[pos]
|
||||
root := nodes[0].rule
|
||||
|
||||
result.Rules = append(result.Rules, root)
|
||||
if len(nodes) > 1 {
|
||||
result.Else[root] = make([]*Rule, len(nodes)-1)
|
||||
for i := 1; i < len(nodes); i++ {
|
||||
result.Else[root][i-1] = nodes[i].rule
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.EarlyExit = tr.values.Len() == 1 && tr.values.Slice()[0].IsGround()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (i *baseDocEqIndex) AllRules(_ ValueResolver) (*IndexResult, error) {
|
||||
tr := newTrieTraversalResult()
|
||||
|
||||
// Walk over the rule trie and accumulate _all_ rules
|
||||
rw := &ruleWalker{result: tr}
|
||||
i.root.Do(rw)
|
||||
|
||||
result := NewIndexResult(i.kind)
|
||||
result.Default = i.defaultRule
|
||||
result.OnlyGroundRefs = i.onlyGroundRefs
|
||||
result.Rules = make([]*Rule, 0, len(tr.ordering))
|
||||
|
||||
for _, pos := range tr.ordering {
|
||||
sort.Slice(tr.unordered[pos], func(i, j int) bool {
|
||||
return tr.unordered[pos][i].prio[1] < tr.unordered[pos][j].prio[1]
|
||||
})
|
||||
nodes := tr.unordered[pos]
|
||||
root := nodes[0].rule
|
||||
result.Rules = append(result.Rules, root)
|
||||
if len(nodes) > 1 {
|
||||
result.Else[root] = make([]*Rule, len(nodes)-1)
|
||||
for i := 1; i < len(nodes); i++ {
|
||||
result.Else[root][i-1] = nodes[i].rule
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.EarlyExit = tr.values.Len() == 1 && tr.values.Slice()[0].IsGround()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type ruleWalker struct {
|
||||
result *trieTraversalResult
|
||||
}
|
||||
|
||||
func (r *ruleWalker) Do(x interface{}) trieWalker {
|
||||
tn := x.(*trieNode)
|
||||
r.result.Add(tn)
|
||||
return r
|
||||
}
|
||||
|
||||
type valueMapper struct {
|
||||
Key string
|
||||
MapValue func(Value) Value
|
||||
}
|
||||
|
||||
type refindex struct {
|
||||
Ref Ref
|
||||
Value Value
|
||||
Mapper *valueMapper
|
||||
}
|
||||
|
||||
type refindices struct {
|
||||
isVirtual func(Ref) bool
|
||||
rules map[*Rule][]*refindex
|
||||
frequency *util.HashMap
|
||||
sorted []Ref
|
||||
}
|
||||
|
||||
func newrefindices(isVirtual func(Ref) bool) *refindices {
|
||||
return &refindices{
|
||||
isVirtual: isVirtual,
|
||||
rules: map[*Rule][]*refindex{},
|
||||
frequency: util.NewHashMap(func(a, b util.T) bool {
|
||||
r1, r2 := a.(Ref), b.(Ref)
|
||||
return r1.Equal(r2)
|
||||
}, func(x util.T) int {
|
||||
return x.(Ref).Hash()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Update attempts to update the refindices for the given expression in the
|
||||
// given rule. If the expression cannot be indexed the update does not affect
|
||||
// the indices.
|
||||
func (i *refindices) Update(rule *Rule, expr *Expr) {
|
||||
|
||||
if expr.Negated {
|
||||
return
|
||||
}
|
||||
|
||||
if len(expr.With) > 0 {
|
||||
// NOTE(tsandall): In the future, we may need to consider expressions
|
||||
// that have with statements applied to them.
|
||||
return
|
||||
}
|
||||
|
||||
op := expr.Operator()
|
||||
|
||||
switch {
|
||||
case op.Equal(Equality.Ref()):
|
||||
i.updateEq(rule, expr)
|
||||
|
||||
case op.Equal(Equal.Ref()) && len(expr.Operands()) == 2:
|
||||
// NOTE(tsandall): if equal() is called with more than two arguments the
|
||||
// output value is being captured in which case the indexer cannot
|
||||
// exclude the rule if the equal() call would return false (because the
|
||||
// false value must still be produced.)
|
||||
i.updateEq(rule, expr)
|
||||
|
||||
case op.Equal(GlobMatch.Ref()) && len(expr.Operands()) == 3:
|
||||
// NOTE(sr): Same as with equal() above -- 4 operands means the output
|
||||
// of `glob.match` is captured and the rule can thus not be excluded.
|
||||
i.updateGlobMatch(rule, expr)
|
||||
}
|
||||
}
|
||||
|
||||
// Sorted returns a sorted list of references that the indices were built from.
|
||||
// References that appear more frequently in the indexed rules are ordered
|
||||
// before less frequently appearing references.
|
||||
func (i *refindices) Sorted() []Ref {
|
||||
|
||||
if i.sorted == nil {
|
||||
counts := make([]int, 0, i.frequency.Len())
|
||||
i.sorted = make([]Ref, 0, i.frequency.Len())
|
||||
|
||||
i.frequency.Iter(func(k, v util.T) bool {
|
||||
counts = append(counts, v.(int))
|
||||
i.sorted = append(i.sorted, k.(Ref))
|
||||
return false
|
||||
})
|
||||
|
||||
sort.Slice(i.sorted, func(a, b int) bool {
|
||||
if counts[a] > counts[b] {
|
||||
return true
|
||||
} else if counts[b] > counts[a] {
|
||||
return false
|
||||
}
|
||||
return i.sorted[a][0].Loc().Compare(i.sorted[b][0].Loc()) < 0
|
||||
})
|
||||
}
|
||||
|
||||
return i.sorted
|
||||
}
|
||||
|
||||
func (i *refindices) Indexed(rule *Rule) bool {
|
||||
return len(i.rules[rule]) > 0
|
||||
}
|
||||
|
||||
func (i *refindices) Value(rule *Rule, ref Ref) Value {
|
||||
if index := i.index(rule, ref); index != nil {
|
||||
return index.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *refindices) Mapper(rule *Rule, ref Ref) *valueMapper {
|
||||
if index := i.index(rule, ref); index != nil {
|
||||
return index.Mapper
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *refindices) updateEq(rule *Rule, expr *Expr) {
|
||||
a, b := expr.Operand(0), expr.Operand(1)
|
||||
args := rule.Head.Args
|
||||
if idx, ok := eqOperandsToRefAndValue(i.isVirtual, args, a, b); ok {
|
||||
i.insert(rule, idx)
|
||||
return
|
||||
}
|
||||
if idx, ok := eqOperandsToRefAndValue(i.isVirtual, args, b, a); ok {
|
||||
i.insert(rule, idx)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) {
|
||||
args := rule.Head.Args
|
||||
|
||||
delim, ok := globDelimiterToString(expr.Operand(1))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if arr := globPatternToArray(expr.Operand(0), delim); arr != nil {
|
||||
// The 3rd operand of glob.match is the value to match. We assume the
|
||||
// 3rd operand was a reference that has been rewritten and bound to a
|
||||
// variable earlier in the query OR a function argument variable.
|
||||
match := expr.Operand(2)
|
||||
if _, ok := match.Value.(Var); ok {
|
||||
var ref Ref
|
||||
for _, other := range i.rules[rule] {
|
||||
if _, ok := other.Value.(Var); ok && other.Value.Compare(match.Value) == 0 {
|
||||
ref = other.Ref
|
||||
}
|
||||
}
|
||||
if ref == nil {
|
||||
for j, arg := range args {
|
||||
if arg.Equal(match) {
|
||||
ref = Ref{FunctionArgRootDocument, IntNumberTerm(j)}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ref != nil {
|
||||
i.insert(rule, &refindex{
|
||||
Ref: ref,
|
||||
Value: arr.Value,
|
||||
Mapper: &valueMapper{
|
||||
Key: delim,
|
||||
MapValue: func(v Value) Value {
|
||||
if s, ok := v.(String); ok {
|
||||
return stringSliceToArray(splitStringEscaped(string(s), delim))
|
||||
}
|
||||
return v
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *refindices) insert(rule *Rule, index *refindex) {
|
||||
|
||||
count, ok := i.frequency.Get(index.Ref)
|
||||
if !ok {
|
||||
count = 0
|
||||
}
|
||||
|
||||
i.frequency.Put(index.Ref, count.(int)+1)
|
||||
|
||||
for pos, other := range i.rules[rule] {
|
||||
if other.Ref.Equal(index.Ref) {
|
||||
i.rules[rule][pos] = index
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
i.rules[rule] = append(i.rules[rule], index)
|
||||
}
|
||||
|
||||
func (i *refindices) index(rule *Rule, ref Ref) *refindex {
|
||||
for _, index := range i.rules[rule] {
|
||||
if index.Ref.Equal(ref) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type trieWalker interface {
|
||||
Do(x interface{}) trieWalker
|
||||
}
|
||||
|
||||
type trieTraversalResult struct {
|
||||
unordered map[int][]*ruleNode
|
||||
ordering []int
|
||||
values Set
|
||||
}
|
||||
|
||||
func newTrieTraversalResult() *trieTraversalResult {
|
||||
return &trieTraversalResult{
|
||||
unordered: map[int][]*ruleNode{},
|
||||
values: NewSet(),
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *trieTraversalResult) Add(t *trieNode) {
|
||||
for _, node := range t.rules {
|
||||
root := node.prio[0]
|
||||
nodes, ok := tr.unordered[root]
|
||||
if !ok {
|
||||
tr.ordering = append(tr.ordering, root)
|
||||
}
|
||||
tr.unordered[root] = append(nodes, node)
|
||||
}
|
||||
if t.values != nil {
|
||||
t.values.Foreach(func(v *Term) { tr.values.Add(v) })
|
||||
}
|
||||
}
|
||||
|
||||
type trieNode struct {
|
||||
ref Ref
|
||||
values Set
|
||||
mappers []*valueMapper
|
||||
next *trieNode
|
||||
any *trieNode
|
||||
undefined *trieNode
|
||||
scalars *util.HashMap
|
||||
array *trieNode
|
||||
rules []*ruleNode
|
||||
}
|
||||
|
||||
func (node *trieNode) String() string {
|
||||
var flags []string
|
||||
flags = append(flags, fmt.Sprintf("self:%p", node))
|
||||
if len(node.ref) > 0 {
|
||||
flags = append(flags, node.ref.String())
|
||||
}
|
||||
if node.next != nil {
|
||||
flags = append(flags, fmt.Sprintf("next:%p", node.next))
|
||||
}
|
||||
if node.any != nil {
|
||||
flags = append(flags, fmt.Sprintf("any:%p", node.any))
|
||||
}
|
||||
if node.undefined != nil {
|
||||
flags = append(flags, fmt.Sprintf("undefined:%p", node.undefined))
|
||||
}
|
||||
if node.array != nil {
|
||||
flags = append(flags, fmt.Sprintf("array:%p", node.array))
|
||||
}
|
||||
if node.scalars.Len() > 0 {
|
||||
buf := make([]string, 0, node.scalars.Len())
|
||||
node.scalars.Iter(func(k, v util.T) bool {
|
||||
key := k.(Value)
|
||||
val := v.(*trieNode)
|
||||
buf = append(buf, fmt.Sprintf("scalar(%v):%p", key, val))
|
||||
return false
|
||||
})
|
||||
sort.Strings(buf)
|
||||
flags = append(flags, strings.Join(buf, " "))
|
||||
}
|
||||
if len(node.rules) > 0 {
|
||||
flags = append(flags, fmt.Sprintf("%d rule(s)", len(node.rules)))
|
||||
}
|
||||
if len(node.mappers) > 0 {
|
||||
flags = append(flags, fmt.Sprintf("%d mapper(s)", len(node.mappers)))
|
||||
}
|
||||
if node.values != nil {
|
||||
if l := node.values.Len(); l > 0 {
|
||||
flags = append(flags, fmt.Sprintf("%d value(s)", l))
|
||||
}
|
||||
}
|
||||
return strings.Join(flags, " ")
|
||||
}
|
||||
|
||||
func (node *trieNode) append(prio [2]int, rule *Rule) {
|
||||
node.rules = append(node.rules, &ruleNode{prio, rule})
|
||||
|
||||
if node.values != nil && rule.Head.Value != nil {
|
||||
node.values.Add(rule.Head.Value)
|
||||
return
|
||||
}
|
||||
|
||||
if node.values == nil && rule.Head.DocKind() == CompleteDoc {
|
||||
node.values = NewSet(rule.Head.Value)
|
||||
}
|
||||
}
|
||||
|
||||
type ruleNode struct {
|
||||
prio [2]int
|
||||
rule *Rule
|
||||
}
|
||||
|
||||
func newTrieNodeImpl() *trieNode {
|
||||
return &trieNode{
|
||||
scalars: util.NewHashMap(valueEq, valueHash),
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieNode) Do(walker trieWalker) {
|
||||
next := walker.Do(node)
|
||||
if next == nil {
|
||||
return
|
||||
}
|
||||
if node.any != nil {
|
||||
node.any.Do(next)
|
||||
}
|
||||
if node.undefined != nil {
|
||||
node.undefined.Do(next)
|
||||
}
|
||||
|
||||
node.scalars.Iter(func(_, v util.T) bool {
|
||||
child := v.(*trieNode)
|
||||
child.Do(next)
|
||||
return false
|
||||
})
|
||||
|
||||
if node.array != nil {
|
||||
node.array.Do(next)
|
||||
}
|
||||
if node.next != nil {
|
||||
node.next.Do(next)
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieNode) Insert(ref Ref, value Value, mapper *valueMapper) *trieNode {
|
||||
|
||||
if node.next == nil {
|
||||
node.next = newTrieNodeImpl()
|
||||
node.next.ref = ref
|
||||
}
|
||||
|
||||
if mapper != nil {
|
||||
node.next.addMapper(mapper)
|
||||
}
|
||||
|
||||
return node.next.insertValue(value)
|
||||
}
|
||||
|
||||
func (node *trieNode) Traverse(resolver ValueResolver, tr *trieTraversalResult) error {
|
||||
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tr.Add(node)
|
||||
|
||||
return node.next.traverse(resolver, tr)
|
||||
}
|
||||
|
||||
func (node *trieNode) addMapper(mapper *valueMapper) {
|
||||
for i := range node.mappers {
|
||||
if node.mappers[i].Key == mapper.Key {
|
||||
return
|
||||
}
|
||||
}
|
||||
node.mappers = append(node.mappers, mapper)
|
||||
}
|
||||
|
||||
func (node *trieNode) insertValue(value Value) *trieNode {
|
||||
|
||||
switch value := value.(type) {
|
||||
case nil:
|
||||
if node.undefined == nil {
|
||||
node.undefined = newTrieNodeImpl()
|
||||
}
|
||||
return node.undefined
|
||||
case Var:
|
||||
if node.any == nil {
|
||||
node.any = newTrieNodeImpl()
|
||||
}
|
||||
return node.any
|
||||
case Null, Boolean, Number, String:
|
||||
child, ok := node.scalars.Get(value)
|
||||
if !ok {
|
||||
child = newTrieNodeImpl()
|
||||
node.scalars.Put(value, child)
|
||||
}
|
||||
return child.(*trieNode)
|
||||
case *Array:
|
||||
if node.array == nil {
|
||||
node.array = newTrieNodeImpl()
|
||||
}
|
||||
return node.array.insertArray(value)
|
||||
}
|
||||
|
||||
panic("illegal value")
|
||||
}
|
||||
|
||||
func (node *trieNode) insertArray(arr *Array) *trieNode {
|
||||
|
||||
if arr.Len() == 0 {
|
||||
return node
|
||||
}
|
||||
|
||||
switch head := arr.Elem(0).Value.(type) {
|
||||
case Var:
|
||||
if node.any == nil {
|
||||
node.any = newTrieNodeImpl()
|
||||
}
|
||||
return node.any.insertArray(arr.Slice(1, -1))
|
||||
case Null, Boolean, Number, String:
|
||||
child, ok := node.scalars.Get(head)
|
||||
if !ok {
|
||||
child = newTrieNodeImpl()
|
||||
node.scalars.Put(head, child)
|
||||
}
|
||||
return child.(*trieNode).insertArray(arr.Slice(1, -1))
|
||||
}
|
||||
|
||||
panic("illegal value")
|
||||
}
|
||||
|
||||
func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) error {
|
||||
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v, err := resolver.Resolve(node.ref)
|
||||
if err != nil {
|
||||
if IsUnknownValueErr(err) {
|
||||
return node.traverseUnknown(resolver, tr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if node.undefined != nil {
|
||||
err = node.undefined.Traverse(resolver, tr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.any != nil {
|
||||
err = node.any.Traverse(resolver, tr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := node.traverseValue(resolver, tr, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range node.mappers {
|
||||
if err := node.traverseValue(resolver, tr, node.mappers[i].MapValue(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalResult, value Value) error {
|
||||
|
||||
switch value := value.(type) {
|
||||
case *Array:
|
||||
if node.array == nil {
|
||||
return nil
|
||||
}
|
||||
return node.array.traverseArray(resolver, tr, value)
|
||||
|
||||
case Null, Boolean, Number, String:
|
||||
child, ok := node.scalars.Get(value)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return child.(*trieNode).Traverse(resolver, tr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalResult, arr *Array) error {
|
||||
|
||||
if arr.Len() == 0 {
|
||||
return node.Traverse(resolver, tr)
|
||||
}
|
||||
|
||||
if node.any != nil {
|
||||
err := node.any.traverseArray(resolver, tr, arr.Slice(1, -1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
head := arr.Elem(0).Value
|
||||
|
||||
if !IsScalar(head) {
|
||||
return nil
|
||||
}
|
||||
|
||||
child, ok := node.scalars.Get(head)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return child.(*trieNode).traverseArray(resolver, tr, arr.Slice(1, -1))
|
||||
}
|
||||
|
||||
func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error {
|
||||
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := node.Traverse(resolver, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := node.undefined.traverseUnknown(resolver, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := node.any.traverseUnknown(resolver, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := node.array.traverseUnknown(resolver, tr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var iterErr error
|
||||
node.scalars.Iter(func(_, v util.T) bool {
|
||||
child := v.(*trieNode)
|
||||
if iterErr = child.traverseUnknown(resolver, tr); iterErr != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return iterErr
|
||||
}
|
||||
|
||||
// If term `a` is one of the function's operands, we store a Ref: `args[0]`
|
||||
// for the argument number. So for `f(x, y) { x = 10; y = 12 }`, we'll
|
||||
// bind `args[0]` and `args[1]` to this rule when called for (x=10) and
|
||||
// (y=12) respectively.
|
||||
func eqOperandsToRefAndValue(isVirtual func(Ref) bool, args []*Term, a, b *Term) (*refindex, bool) {
|
||||
switch v := a.Value.(type) {
|
||||
case Var:
|
||||
for i, arg := range args {
|
||||
if arg.Value.Compare(v) == 0 {
|
||||
if bval, ok := indexValue(b); ok {
|
||||
return &refindex{Ref: Ref{FunctionArgRootDocument, IntNumberTerm(i)}, Value: bval}, true
|
||||
}
|
||||
}
|
||||
}
|
||||
case Ref:
|
||||
if !RootDocumentNames.Contains(v[0]) {
|
||||
return nil, false
|
||||
}
|
||||
if isVirtual(v) {
|
||||
return nil, false
|
||||
}
|
||||
if v.IsNested() || !v.IsGround() {
|
||||
return nil, false
|
||||
}
|
||||
if bval, ok := indexValue(b); ok {
|
||||
return &refindex{Ref: v, Value: bval}, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func indexValue(b *Term) (Value, bool) {
|
||||
switch b := b.Value.(type) {
|
||||
case Null, Boolean, Number, String, Var:
|
||||
return b, true
|
||||
case *Array:
|
||||
stop := false
|
||||
first := true
|
||||
vis := NewGenericVisitor(func(x interface{}) bool {
|
||||
if first {
|
||||
first = false
|
||||
return false
|
||||
}
|
||||
switch x.(type) {
|
||||
// No nested structures or values that require evaluation (other than var).
|
||||
case *Array, Object, Set, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Ref:
|
||||
stop = true
|
||||
}
|
||||
return stop
|
||||
})
|
||||
vis.Walk(b)
|
||||
if !stop {
|
||||
return b, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func globDelimiterToString(delim *Term) (string, bool) {
|
||||
|
||||
arr, ok := delim.Value.(*Array)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
var result string
|
||||
|
||||
if arr.Len() == 0 {
|
||||
result = "."
|
||||
} else {
|
||||
for i := 0; i < arr.Len(); i++ {
|
||||
term := arr.Elem(i)
|
||||
s, ok := term.Value.(String)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
result += string(s)
|
||||
}
|
||||
}
|
||||
|
||||
return result, true
|
||||
}
|
||||
|
||||
func globPatternToArray(pattern *Term, delim string) *Term {
|
||||
|
||||
s, ok := pattern.Value.(String)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := splitStringEscaped(string(s), delim)
|
||||
arr := make([]*Term, len(parts))
|
||||
|
||||
for i := range parts {
|
||||
if parts[i] == "*" {
|
||||
arr[i] = VarTerm("$globwildcard")
|
||||
} else {
|
||||
var escaped bool
|
||||
for _, c := range parts[i] {
|
||||
if c == '\\' {
|
||||
escaped = !escaped
|
||||
continue
|
||||
}
|
||||
if !escaped {
|
||||
switch c {
|
||||
case '[', '?', '{', '*':
|
||||
// TODO(tsandall): super glob and character pattern
|
||||
// matching not supported yet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
escaped = false
|
||||
}
|
||||
arr[i] = StringTerm(parts[i])
|
||||
}
|
||||
}
|
||||
|
||||
return NewTerm(NewArray(arr...))
|
||||
}
|
||||
|
||||
// splits s on characters in delim except if delim characters have been escaped
|
||||
// with reverse solidus.
|
||||
func splitStringEscaped(s string, delim string) []string {
|
||||
|
||||
var last, curr int
|
||||
var escaped bool
|
||||
var result []string
|
||||
|
||||
for ; curr < len(s); curr++ {
|
||||
if s[curr] == '\\' || escaped {
|
||||
escaped = !escaped
|
||||
continue
|
||||
}
|
||||
if strings.ContainsRune(delim, rune(s[curr])) {
|
||||
result = append(result, s[last:curr])
|
||||
last = curr + 1
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, s[last:])
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func stringSliceToArray(s []string) *Array {
|
||||
arr := make([]*Term, len(s))
|
||||
for i, v := range s {
|
||||
arr[i] = StringTerm(v)
|
||||
}
|
||||
return NewArray(arr...)
|
||||
return v1.NewIndexResult(kind)
|
||||
}
|
||||
|
||||
455
vendor/github.com/open-policy-agent/opa/ast/internal/scanner/scanner.go
generated
vendored
455
vendor/github.com/open-policy-agent/opa/ast/internal/scanner/scanner.go
generated
vendored
@@ -1,455 +0,0 @@
|
||||
// Copyright 2020 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast/internal/tokens"
|
||||
)
|
||||
|
||||
const bom = 0xFEFF
|
||||
|
||||
// Scanner is used to tokenize an input stream of
|
||||
// Rego source code.
|
||||
type Scanner struct {
|
||||
offset int
|
||||
row int
|
||||
col int
|
||||
bs []byte
|
||||
curr rune
|
||||
width int
|
||||
errors []Error
|
||||
keywords map[string]tokens.Token
|
||||
tabs []int
|
||||
regoV1Compatible bool
|
||||
}
|
||||
|
||||
// Error represents a scanner error.
|
||||
type Error struct {
|
||||
Pos Position
|
||||
Message string
|
||||
}
|
||||
|
||||
// Position represents a point in the scanned source code.
|
||||
type Position struct {
|
||||
Offset int // start offset in bytes
|
||||
End int // end offset in bytes
|
||||
Row int // line number computed in bytes
|
||||
Col int // column number computed in bytes
|
||||
Tabs []int // positions of any tabs preceding Col
|
||||
}
|
||||
|
||||
// New returns an initialized scanner that will scan
|
||||
// through the source code provided by the io.Reader.
|
||||
func New(r io.Reader) (*Scanner, error) {
|
||||
|
||||
bs, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Scanner{
|
||||
offset: 0,
|
||||
row: 1,
|
||||
col: 0,
|
||||
bs: bs,
|
||||
curr: -1,
|
||||
width: 0,
|
||||
keywords: tokens.Keywords(),
|
||||
tabs: []int{},
|
||||
}
|
||||
|
||||
s.next()
|
||||
|
||||
if s.curr == bom {
|
||||
s.next()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Bytes returns the raw bytes for the full source
|
||||
// which the scanner has read in.
|
||||
func (s *Scanner) Bytes() []byte {
|
||||
return s.bs
|
||||
}
|
||||
|
||||
// String returns a human readable string of the current scanner state.
|
||||
func (s *Scanner) String() string {
|
||||
return fmt.Sprintf("<curr: %q, offset: %d, len: %d>", s.curr, s.offset, len(s.bs))
|
||||
}
|
||||
|
||||
// Keyword will return a token for the passed in
|
||||
// literal value. If the value is a Rego keyword
|
||||
// then the appropriate token is returned. Everything
|
||||
// else is an Ident.
|
||||
func (s *Scanner) Keyword(lit string) tokens.Token {
|
||||
if tok, ok := s.keywords[lit]; ok {
|
||||
return tok
|
||||
}
|
||||
return tokens.Ident
|
||||
}
|
||||
|
||||
// AddKeyword adds a string -> token mapping to this Scanner instance.
|
||||
func (s *Scanner) AddKeyword(kw string, tok tokens.Token) {
|
||||
s.keywords[kw] = tok
|
||||
|
||||
switch tok {
|
||||
case tokens.Every: // importing 'every' means also importing 'in'
|
||||
s.keywords["in"] = tokens.In
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) HasKeyword(keywords map[string]tokens.Token) bool {
|
||||
for kw := range s.keywords {
|
||||
if _, ok := keywords[kw]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Scanner) SetRegoV1Compatible() {
|
||||
s.regoV1Compatible = true
|
||||
}
|
||||
|
||||
func (s *Scanner) RegoV1Compatible() bool {
|
||||
return s.regoV1Compatible
|
||||
}
|
||||
|
||||
// WithKeywords returns a new copy of the Scanner struct `s`, with the set
|
||||
// of known keywords being that of `s` with `kws` added.
|
||||
func (s *Scanner) WithKeywords(kws map[string]tokens.Token) *Scanner {
|
||||
cpy := *s
|
||||
cpy.keywords = make(map[string]tokens.Token, len(s.keywords)+len(kws))
|
||||
for kw, tok := range s.keywords {
|
||||
cpy.AddKeyword(kw, tok)
|
||||
}
|
||||
for k, t := range kws {
|
||||
cpy.AddKeyword(k, t)
|
||||
}
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// WithoutKeywords returns a new copy of the Scanner struct `s`, with the
|
||||
// set of known keywords being that of `s` with `kws` removed.
|
||||
// The previously known keywords are returned for a convenient reset.
|
||||
func (s *Scanner) WithoutKeywords(kws map[string]tokens.Token) (*Scanner, map[string]tokens.Token) {
|
||||
cpy := *s
|
||||
kw := s.keywords
|
||||
cpy.keywords = make(map[string]tokens.Token, len(s.keywords)-len(kws))
|
||||
for kw, tok := range s.keywords {
|
||||
if _, ok := kws[kw]; !ok {
|
||||
cpy.AddKeyword(kw, tok)
|
||||
}
|
||||
}
|
||||
return &cpy, kw
|
||||
}
|
||||
|
||||
// Scan will increment the scanners position in the source
|
||||
// code until the next token is found. The token, starting position
|
||||
// of the token, string literal, and any errors encountered are
|
||||
// returned. A token will always be returned, the caller must check
|
||||
// for any errors before using the other values.
|
||||
func (s *Scanner) Scan() (tokens.Token, Position, string, []Error) {
|
||||
|
||||
pos := Position{Offset: s.offset - s.width, Row: s.row, Col: s.col, Tabs: s.tabs}
|
||||
var tok tokens.Token
|
||||
var lit string
|
||||
|
||||
if s.isWhitespace() {
|
||||
lit = string(s.curr)
|
||||
s.next()
|
||||
tok = tokens.Whitespace
|
||||
} else if isLetter(s.curr) {
|
||||
lit = s.scanIdentifier()
|
||||
tok = s.Keyword(lit)
|
||||
} else if isDecimal(s.curr) {
|
||||
lit = s.scanNumber()
|
||||
tok = tokens.Number
|
||||
} else {
|
||||
ch := s.curr
|
||||
s.next()
|
||||
switch ch {
|
||||
case -1:
|
||||
tok = tokens.EOF
|
||||
case '#':
|
||||
lit = s.scanComment()
|
||||
tok = tokens.Comment
|
||||
case '"':
|
||||
lit = s.scanString()
|
||||
tok = tokens.String
|
||||
case '`':
|
||||
lit = s.scanRawString()
|
||||
tok = tokens.String
|
||||
case '[':
|
||||
tok = tokens.LBrack
|
||||
case ']':
|
||||
tok = tokens.RBrack
|
||||
case '{':
|
||||
tok = tokens.LBrace
|
||||
case '}':
|
||||
tok = tokens.RBrace
|
||||
case '(':
|
||||
tok = tokens.LParen
|
||||
case ')':
|
||||
tok = tokens.RParen
|
||||
case ',':
|
||||
tok = tokens.Comma
|
||||
case ':':
|
||||
if s.curr == '=' {
|
||||
s.next()
|
||||
tok = tokens.Assign
|
||||
} else {
|
||||
tok = tokens.Colon
|
||||
}
|
||||
case '+':
|
||||
tok = tokens.Add
|
||||
case '-':
|
||||
tok = tokens.Sub
|
||||
case '*':
|
||||
tok = tokens.Mul
|
||||
case '/':
|
||||
tok = tokens.Quo
|
||||
case '%':
|
||||
tok = tokens.Rem
|
||||
case '&':
|
||||
tok = tokens.And
|
||||
case '|':
|
||||
tok = tokens.Or
|
||||
case '=':
|
||||
if s.curr == '=' {
|
||||
s.next()
|
||||
tok = tokens.Equal
|
||||
} else {
|
||||
tok = tokens.Unify
|
||||
}
|
||||
case '>':
|
||||
if s.curr == '=' {
|
||||
s.next()
|
||||
tok = tokens.Gte
|
||||
} else {
|
||||
tok = tokens.Gt
|
||||
}
|
||||
case '<':
|
||||
if s.curr == '=' {
|
||||
s.next()
|
||||
tok = tokens.Lte
|
||||
} else {
|
||||
tok = tokens.Lt
|
||||
}
|
||||
case '!':
|
||||
if s.curr == '=' {
|
||||
s.next()
|
||||
tok = tokens.Neq
|
||||
} else {
|
||||
s.error("illegal ! character")
|
||||
}
|
||||
case ';':
|
||||
tok = tokens.Semicolon
|
||||
case '.':
|
||||
tok = tokens.Dot
|
||||
}
|
||||
}
|
||||
|
||||
pos.End = s.offset - s.width
|
||||
errs := s.errors
|
||||
s.errors = nil
|
||||
|
||||
return tok, pos, lit, errs
|
||||
}
|
||||
|
||||
func (s *Scanner) scanIdentifier() string {
|
||||
start := s.offset - 1
|
||||
for isLetter(s.curr) || isDigit(s.curr) {
|
||||
s.next()
|
||||
}
|
||||
return string(s.bs[start : s.offset-1])
|
||||
}
|
||||
|
||||
func (s *Scanner) scanNumber() string {
|
||||
|
||||
start := s.offset - 1
|
||||
|
||||
if s.curr != '.' {
|
||||
for isDecimal(s.curr) {
|
||||
s.next()
|
||||
}
|
||||
}
|
||||
|
||||
if s.curr == '.' {
|
||||
s.next()
|
||||
var found bool
|
||||
for isDecimal(s.curr) {
|
||||
s.next()
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
s.error("expected fraction")
|
||||
}
|
||||
}
|
||||
|
||||
if lower(s.curr) == 'e' {
|
||||
s.next()
|
||||
if s.curr == '+' || s.curr == '-' {
|
||||
s.next()
|
||||
}
|
||||
var found bool
|
||||
for isDecimal(s.curr) {
|
||||
s.next()
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
s.error("expected exponent")
|
||||
}
|
||||
}
|
||||
|
||||
// Scan any digits following the decimals to get the
|
||||
// entire invalid number/identifier.
|
||||
// Example: 0a2b should be a single invalid number "0a2b"
|
||||
// rather than a number "0", followed by identifier "a2b".
|
||||
if isLetter(s.curr) {
|
||||
s.error("illegal number format")
|
||||
for isLetter(s.curr) || isDigit(s.curr) {
|
||||
s.next()
|
||||
}
|
||||
}
|
||||
|
||||
return string(s.bs[start : s.offset-1])
|
||||
}
|
||||
|
||||
func (s *Scanner) scanString() string {
|
||||
start := s.literalStart()
|
||||
for {
|
||||
ch := s.curr
|
||||
|
||||
if ch == '\n' || ch < 0 {
|
||||
s.error("non-terminated string")
|
||||
break
|
||||
}
|
||||
|
||||
s.next()
|
||||
|
||||
if ch == '"' {
|
||||
break
|
||||
}
|
||||
|
||||
if ch == '\\' {
|
||||
switch s.curr {
|
||||
case '\\', '"', '/', 'b', 'f', 'n', 'r', 't':
|
||||
s.next()
|
||||
case 'u':
|
||||
s.next()
|
||||
s.next()
|
||||
s.next()
|
||||
s.next()
|
||||
default:
|
||||
s.error("illegal escape sequence")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string(s.bs[start : s.offset-1])
|
||||
}
|
||||
|
||||
func (s *Scanner) scanRawString() string {
|
||||
start := s.literalStart()
|
||||
for {
|
||||
ch := s.curr
|
||||
s.next()
|
||||
if ch == '`' {
|
||||
break
|
||||
} else if ch < 0 {
|
||||
s.error("non-terminated string")
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(s.bs[start : s.offset-1])
|
||||
}
|
||||
|
||||
func (s *Scanner) scanComment() string {
|
||||
start := s.literalStart()
|
||||
for s.curr != '\n' && s.curr != -1 {
|
||||
s.next()
|
||||
}
|
||||
end := s.offset - 1
|
||||
// Trim carriage returns that precede the newline
|
||||
if s.offset > 1 && s.bs[s.offset-2] == '\r' {
|
||||
end = end - 1
|
||||
}
|
||||
return string(s.bs[start:end])
|
||||
}
|
||||
|
||||
func (s *Scanner) next() {
|
||||
|
||||
if s.offset >= len(s.bs) {
|
||||
s.curr = -1
|
||||
s.offset = len(s.bs) + 1
|
||||
return
|
||||
}
|
||||
|
||||
s.curr = rune(s.bs[s.offset])
|
||||
s.width = 1
|
||||
|
||||
if s.curr == 0 {
|
||||
s.error("illegal null character")
|
||||
} else if s.curr >= utf8.RuneSelf {
|
||||
s.curr, s.width = utf8.DecodeRune(s.bs[s.offset:])
|
||||
if s.curr == utf8.RuneError && s.width == 1 {
|
||||
s.error("illegal utf-8 character")
|
||||
} else if s.curr == bom && s.offset > 0 {
|
||||
s.error("illegal byte-order mark")
|
||||
}
|
||||
}
|
||||
|
||||
s.offset += s.width
|
||||
|
||||
if s.curr == '\n' {
|
||||
s.row++
|
||||
s.col = 0
|
||||
s.tabs = []int{}
|
||||
} else {
|
||||
s.col++
|
||||
if s.curr == '\t' {
|
||||
s.tabs = append(s.tabs, s.col)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) literalStart() int {
|
||||
// The current offset is at the first character past the literal delimiter (#, ", `, etc.)
|
||||
// Need to subtract width of first character (plus one for the delimiter).
|
||||
return s.offset - (s.width + 1)
|
||||
}
|
||||
|
||||
// From the Go scanner (src/go/scanner/scanner.go)
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return 'a' <= lower(ch) && lower(ch) <= 'z' || ch == '_'
|
||||
}
|
||||
|
||||
func isDigit(ch rune) bool {
|
||||
return isDecimal(ch) || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
|
||||
}
|
||||
|
||||
func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }
|
||||
|
||||
func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter
|
||||
|
||||
func (s *Scanner) isWhitespace() bool {
|
||||
return s.curr == ' ' || s.curr == '\t' || s.curr == '\n' || s.curr == '\r'
|
||||
}
|
||||
|
||||
func (s *Scanner) error(reason string) {
|
||||
s.errors = append(s.errors, Error{Pos: Position{
|
||||
Offset: s.offset,
|
||||
Row: s.row,
|
||||
Col: s.col,
|
||||
}, Message: reason})
|
||||
}
|
||||
151
vendor/github.com/open-policy-agent/opa/ast/internal/tokens/tokens.go
generated
vendored
151
vendor/github.com/open-policy-agent/opa/ast/internal/tokens/tokens.go
generated
vendored
@@ -1,151 +0,0 @@
|
||||
// Copyright 2020 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tokens
|
||||
|
||||
// Token represents a single Rego source code token
|
||||
// for use by the Parser.
|
||||
type Token int
|
||||
|
||||
func (t Token) String() string {
|
||||
if t < 0 || int(t) >= len(strings) {
|
||||
return "unknown"
|
||||
}
|
||||
return strings[t]
|
||||
}
|
||||
|
||||
// All tokens must be defined here
|
||||
const (
|
||||
Illegal Token = iota
|
||||
EOF
|
||||
Whitespace
|
||||
Ident
|
||||
Comment
|
||||
|
||||
Package
|
||||
Import
|
||||
As
|
||||
Default
|
||||
Else
|
||||
Not
|
||||
Some
|
||||
With
|
||||
Null
|
||||
True
|
||||
False
|
||||
|
||||
Number
|
||||
String
|
||||
|
||||
LBrack
|
||||
RBrack
|
||||
LBrace
|
||||
RBrace
|
||||
LParen
|
||||
RParen
|
||||
Comma
|
||||
Colon
|
||||
|
||||
Add
|
||||
Sub
|
||||
Mul
|
||||
Quo
|
||||
Rem
|
||||
And
|
||||
Or
|
||||
Unify
|
||||
Equal
|
||||
Assign
|
||||
In
|
||||
Neq
|
||||
Gt
|
||||
Lt
|
||||
Gte
|
||||
Lte
|
||||
Dot
|
||||
Semicolon
|
||||
|
||||
Every
|
||||
Contains
|
||||
If
|
||||
)
|
||||
|
||||
var strings = [...]string{
|
||||
Illegal: "illegal",
|
||||
EOF: "eof",
|
||||
Whitespace: "whitespace",
|
||||
Comment: "comment",
|
||||
Ident: "identifier",
|
||||
Package: "package",
|
||||
Import: "import",
|
||||
As: "as",
|
||||
Default: "default",
|
||||
Else: "else",
|
||||
Not: "not",
|
||||
Some: "some",
|
||||
With: "with",
|
||||
Null: "null",
|
||||
True: "true",
|
||||
False: "false",
|
||||
Number: "number",
|
||||
String: "string",
|
||||
LBrack: "[",
|
||||
RBrack: "]",
|
||||
LBrace: "{",
|
||||
RBrace: "}",
|
||||
LParen: "(",
|
||||
RParen: ")",
|
||||
Comma: ",",
|
||||
Colon: ":",
|
||||
Add: "plus",
|
||||
Sub: "minus",
|
||||
Mul: "mul",
|
||||
Quo: "div",
|
||||
Rem: "rem",
|
||||
And: "and",
|
||||
Or: "or",
|
||||
Unify: "eq",
|
||||
Equal: "equal",
|
||||
Assign: "assign",
|
||||
In: "in",
|
||||
Neq: "neq",
|
||||
Gt: "gt",
|
||||
Lt: "lt",
|
||||
Gte: "gte",
|
||||
Lte: "lte",
|
||||
Dot: ".",
|
||||
Semicolon: ";",
|
||||
Every: "every",
|
||||
Contains: "contains",
|
||||
If: "if",
|
||||
}
|
||||
|
||||
var keywords = map[string]Token{
|
||||
"package": Package,
|
||||
"import": Import,
|
||||
"as": As,
|
||||
"default": Default,
|
||||
"else": Else,
|
||||
"not": Not,
|
||||
"some": Some,
|
||||
"with": With,
|
||||
"null": Null,
|
||||
"true": True,
|
||||
"false": False,
|
||||
}
|
||||
|
||||
// Keywords returns a copy of the default string -> Token keyword map.
|
||||
func Keywords() map[string]Token {
|
||||
cpy := make(map[string]Token, len(keywords))
|
||||
for k, v := range keywords {
|
||||
cpy[k] = v
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// IsKeyword returns if a token is a keyword
|
||||
func IsKeyword(tok Token) bool {
|
||||
_, ok := keywords[strings[tok]]
|
||||
return ok
|
||||
}
|
||||
24
vendor/github.com/open-policy-agent/opa/ast/interning.go
generated
vendored
Normal file
24
vendor/github.com/open-policy-agent/opa/ast/interning.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2024 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
func InternedBooleanTerm(b bool) *Term {
|
||||
return v1.InternedBooleanTerm(b)
|
||||
}
|
||||
|
||||
// InternedIntNumberTerm returns a term with the given integer value. The term is
|
||||
// cached between -1 to 512, and for values outside of that range, this function
|
||||
// is equivalent to ast.IntNumberTerm.
|
||||
func InternedIntNumberTerm(i int) *Term {
|
||||
return v1.InternedIntNumberTerm(i)
|
||||
}
|
||||
|
||||
func HasInternedIntNumberTerm(i int) bool {
|
||||
return v1.HasInternedIntNumberTerm(i)
|
||||
}
|
||||
8
vendor/github.com/open-policy-agent/opa/ast/json/doc.go
generated
vendored
Normal file
8
vendor/github.com/open-policy-agent/opa/ast/json/doc.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2024 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Deprecated: This package is intended for older projects transitioning from OPA v0.x and will remain for the lifetime of OPA v1.x, but its use is not recommended.
|
||||
// For newer features and behaviours, such as defaulting to the Rego v1 syntax, use the corresponding components in the [github.com/open-policy-agent/opa/v1] package instead.
|
||||
// See https://www.openpolicyagent.org/docs/latest/v0-compatibility/ for more information.
|
||||
package json
|
||||
31
vendor/github.com/open-policy-agent/opa/ast/json/json.go
generated
vendored
31
vendor/github.com/open-policy-agent/opa/ast/json/json.go
generated
vendored
@@ -1,36 +1,15 @@
|
||||
package json
|
||||
|
||||
import v1 "github.com/open-policy-agent/opa/v1/ast/json"
|
||||
|
||||
// Options defines the options for JSON operations,
|
||||
// currently only marshaling can be configured
|
||||
type Options struct {
|
||||
MarshalOptions MarshalOptions
|
||||
}
|
||||
type Options = v1.Options
|
||||
|
||||
// MarshalOptions defines the options for JSON marshaling,
|
||||
// currently only toggling the marshaling of location information is supported
|
||||
type MarshalOptions struct {
|
||||
// IncludeLocation toggles the marshaling of location information
|
||||
IncludeLocation NodeToggle
|
||||
// IncludeLocationText additionally/optionally includes the text of the location
|
||||
IncludeLocationText bool
|
||||
// ExcludeLocationFile additionally/optionally excludes the file of the location
|
||||
// Note that this is inverted (i.e. not "include" as the default needs to remain false)
|
||||
ExcludeLocationFile bool
|
||||
}
|
||||
type MarshalOptions = v1.MarshalOptions
|
||||
|
||||
// NodeToggle is a generic struct to allow the toggling of
|
||||
// settings for different ast node types
|
||||
type NodeToggle struct {
|
||||
Term bool
|
||||
Package bool
|
||||
Comment bool
|
||||
Import bool
|
||||
Rule bool
|
||||
Head bool
|
||||
Expr bool
|
||||
SomeDecl bool
|
||||
Every bool
|
||||
With bool
|
||||
Annotations bool
|
||||
AnnotationsRef bool
|
||||
}
|
||||
type NodeToggle = v1.NodeToggle
|
||||
|
||||
134
vendor/github.com/open-policy-agent/opa/ast/location/location.go
generated
vendored
134
vendor/github.com/open-policy-agent/opa/ast/location/location.go
generated
vendored
@@ -1,134 +0,0 @@
|
||||
// Package location defines locations in Rego source code.
|
||||
package location
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
astJSON "github.com/open-policy-agent/opa/ast/json"
|
||||
)
|
||||
|
||||
// Location records a position in source code
|
||||
type Location struct {
|
||||
Text []byte `json:"-"` // The original text fragment from the source.
|
||||
File string `json:"file"` // The name of the source file (which may be empty).
|
||||
Row int `json:"row"` // The line in the source.
|
||||
Col int `json:"col"` // The column in the row.
|
||||
Offset int `json:"-"` // The byte offset for the location in the source.
|
||||
|
||||
// JSONOptions specifies options for marshaling and unmarshalling of locations
|
||||
JSONOptions astJSON.Options
|
||||
|
||||
Tabs []int `json:"-"` // The column offsets of tabs in the source.
|
||||
}
|
||||
|
||||
// NewLocation returns a new Location object.
|
||||
func NewLocation(text []byte, file string, row int, col int) *Location {
|
||||
return &Location{Text: text, File: file, Row: row, Col: col}
|
||||
}
|
||||
|
||||
// Equal checks if two locations are equal to each other.
|
||||
func (loc *Location) Equal(other *Location) bool {
|
||||
return bytes.Equal(loc.Text, other.Text) &&
|
||||
loc.File == other.File &&
|
||||
loc.Row == other.Row &&
|
||||
loc.Col == other.Col
|
||||
}
|
||||
|
||||
// Errorf returns a new error value with a message formatted to include the location
|
||||
// info (e.g., line, column, filename, etc.)
|
||||
func (loc *Location) Errorf(f string, a ...interface{}) error {
|
||||
return errors.New(loc.Format(f, a...))
|
||||
}
|
||||
|
||||
// Wrapf returns a new error value that wraps an existing error with a message formatted
|
||||
// to include the location info (e.g., line, column, filename, etc.)
|
||||
func (loc *Location) Wrapf(err error, f string, a ...interface{}) error {
|
||||
return fmt.Errorf(loc.Format(f, a...)+": %w", err)
|
||||
}
|
||||
|
||||
// Format returns a formatted string prefixed with the location information.
|
||||
func (loc *Location) Format(f string, a ...interface{}) string {
|
||||
if len(loc.File) > 0 {
|
||||
f = fmt.Sprintf("%v:%v: %v", loc.File, loc.Row, f)
|
||||
} else {
|
||||
f = fmt.Sprintf("%v:%v: %v", loc.Row, loc.Col, f)
|
||||
}
|
||||
return fmt.Sprintf(f, a...)
|
||||
}
|
||||
|
||||
func (loc *Location) String() string {
|
||||
if len(loc.File) > 0 {
|
||||
return fmt.Sprintf("%v:%v", loc.File, loc.Row)
|
||||
}
|
||||
if len(loc.Text) > 0 {
|
||||
return string(loc.Text)
|
||||
}
|
||||
return fmt.Sprintf("%v:%v", loc.Row, loc.Col)
|
||||
}
|
||||
|
||||
// Compare returns -1, 0, or 1 to indicate if this loc is less than, equal to,
|
||||
// or greater than the other. Comparison is performed on the file, row, and
|
||||
// column of the Location (but not on the text.) Nil locations are greater than
|
||||
// non-nil locations.
|
||||
func (loc *Location) Compare(other *Location) int {
|
||||
if loc == nil && other == nil {
|
||||
return 0
|
||||
} else if loc == nil {
|
||||
return 1
|
||||
} else if other == nil {
|
||||
return -1
|
||||
} else if loc.File < other.File {
|
||||
return -1
|
||||
} else if loc.File > other.File {
|
||||
return 1
|
||||
} else if loc.Row < other.Row {
|
||||
return -1
|
||||
} else if loc.Row > other.Row {
|
||||
return 1
|
||||
} else if loc.Col < other.Col {
|
||||
return -1
|
||||
} else if loc.Col > other.Col {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (loc *Location) MarshalJSON() ([]byte, error) {
|
||||
// structs are used here to preserve the field ordering of the original Location struct
|
||||
if loc.JSONOptions.MarshalOptions.ExcludeLocationFile {
|
||||
data := struct {
|
||||
Row int `json:"row"`
|
||||
Col int `json:"col"`
|
||||
Text []byte `json:"text,omitempty"`
|
||||
}{
|
||||
Row: loc.Row,
|
||||
Col: loc.Col,
|
||||
}
|
||||
|
||||
if loc.JSONOptions.MarshalOptions.IncludeLocationText {
|
||||
data.Text = loc.Text
|
||||
}
|
||||
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
data := struct {
|
||||
File string `json:"file"`
|
||||
Row int `json:"row"`
|
||||
Col int `json:"col"`
|
||||
Text []byte `json:"text,omitempty"`
|
||||
}{
|
||||
Row: loc.Row,
|
||||
Col: loc.Col,
|
||||
File: loc.File,
|
||||
}
|
||||
|
||||
if loc.JSONOptions.MarshalOptions.IncludeLocationText {
|
||||
data.Text = loc.Text
|
||||
}
|
||||
|
||||
return json.Marshal(data)
|
||||
}
|
||||
121
vendor/github.com/open-policy-agent/opa/ast/map.go
generated
vendored
121
vendor/github.com/open-policy-agent/opa/ast/map.go
generated
vendored
@@ -5,129 +5,14 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// ValueMap represents a key/value map between AST term values. Any type of term
|
||||
// can be used as a key in the map.
|
||||
type ValueMap struct {
|
||||
hashMap *util.HashMap
|
||||
}
|
||||
type ValueMap = v1.ValueMap
|
||||
|
||||
// NewValueMap returns a new ValueMap.
|
||||
func NewValueMap() *ValueMap {
|
||||
vs := &ValueMap{
|
||||
hashMap: util.NewHashMap(valueEq, valueHash),
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// MarshalJSON provides a custom marshaller for the ValueMap which
|
||||
// will include the key, value, and value type.
|
||||
func (vs *ValueMap) MarshalJSON() ([]byte, error) {
|
||||
var tmp []map[string]interface{}
|
||||
vs.Iter(func(k Value, v Value) bool {
|
||||
tmp = append(tmp, map[string]interface{}{
|
||||
"name": k.String(),
|
||||
"type": TypeName(v),
|
||||
"value": v,
|
||||
})
|
||||
return false
|
||||
})
|
||||
return json.Marshal(tmp)
|
||||
}
|
||||
|
||||
// Copy returns a shallow copy of the ValueMap.
|
||||
func (vs *ValueMap) Copy() *ValueMap {
|
||||
if vs == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := NewValueMap()
|
||||
cpy.hashMap = vs.hashMap.Copy()
|
||||
return cpy
|
||||
}
|
||||
|
||||
// Equal returns true if this ValueMap equals the other.
|
||||
func (vs *ValueMap) Equal(other *ValueMap) bool {
|
||||
if vs == nil {
|
||||
return other == nil || other.Len() == 0
|
||||
}
|
||||
if other == nil {
|
||||
return vs == nil || vs.Len() == 0
|
||||
}
|
||||
return vs.hashMap.Equal(other.hashMap)
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the map.
|
||||
func (vs *ValueMap) Len() int {
|
||||
if vs == nil {
|
||||
return 0
|
||||
}
|
||||
return vs.hashMap.Len()
|
||||
}
|
||||
|
||||
// Get returns the value in the map for k.
|
||||
func (vs *ValueMap) Get(k Value) Value {
|
||||
if vs != nil {
|
||||
if v, ok := vs.hashMap.Get(k); ok {
|
||||
return v.(Value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash returns a hash code for this ValueMap.
|
||||
func (vs *ValueMap) Hash() int {
|
||||
if vs == nil {
|
||||
return 0
|
||||
}
|
||||
return vs.hashMap.Hash()
|
||||
}
|
||||
|
||||
// Iter calls the iter function for each key/value pair in the map. If the iter
|
||||
// function returns true, iteration stops.
|
||||
func (vs *ValueMap) Iter(iter func(Value, Value) bool) bool {
|
||||
if vs == nil {
|
||||
return false
|
||||
}
|
||||
return vs.hashMap.Iter(func(kt, vt util.T) bool {
|
||||
k := kt.(Value)
|
||||
v := vt.(Value)
|
||||
return iter(k, v)
|
||||
})
|
||||
}
|
||||
|
||||
// Put inserts a key k into the map with value v.
|
||||
func (vs *ValueMap) Put(k, v Value) {
|
||||
if vs == nil {
|
||||
panic("put on nil value map")
|
||||
}
|
||||
vs.hashMap.Put(k, v)
|
||||
}
|
||||
|
||||
// Delete removes a key k from the map.
|
||||
func (vs *ValueMap) Delete(k Value) {
|
||||
if vs == nil {
|
||||
return
|
||||
}
|
||||
vs.hashMap.Delete(k)
|
||||
}
|
||||
|
||||
func (vs *ValueMap) String() string {
|
||||
if vs == nil {
|
||||
return "{}"
|
||||
}
|
||||
return vs.hashMap.String()
|
||||
}
|
||||
|
||||
func valueHash(v util.T) int {
|
||||
return v.(Value).Hash()
|
||||
}
|
||||
|
||||
func valueEq(a, b util.T) bool {
|
||||
av := a.(Value)
|
||||
bv := b.(Value)
|
||||
return av.Compare(bv) == 0
|
||||
return v1.NewValueMap()
|
||||
}
|
||||
|
||||
11
vendor/github.com/open-policy-agent/opa/ast/marshal.go
generated
vendored
11
vendor/github.com/open-policy-agent/opa/ast/marshal.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
astJSON "github.com/open-policy-agent/opa/ast/json"
|
||||
)
|
||||
|
||||
// customJSON is an interface that can be implemented by AST nodes that
|
||||
// allows the parser to set options for JSON operations on that node.
|
||||
type customJSON interface {
|
||||
setJSONOptions(astJSON.Options)
|
||||
}
|
||||
2712
vendor/github.com/open-policy-agent/opa/ast/parser.go
generated
vendored
2712
vendor/github.com/open-policy-agent/opa/ast/parser.go
generated
vendored
File diff suppressed because it is too large
Load Diff
598
vendor/github.com/open-policy-agent/opa/ast/parser_ext.go
generated
vendored
598
vendor/github.com/open-policy-agent/opa/ast/parser_ext.go
generated
vendored
@@ -1,24 +1,14 @@
|
||||
// Copyright 2016 The OPA Authors. All rights reserved.
|
||||
// Copyright 2024 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains extra functions for parsing Rego.
|
||||
// Most of the parsing is handled by the code in parser.go,
|
||||
// however, there are additional utilities that are
|
||||
// helpful for dealing with Rego source inputs (e.g., REPL
|
||||
// statements, source files, etc.)
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast/internal/tokens"
|
||||
astJSON "github.com/open-policy-agent/opa/ast/json"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// MustParseBody returns a parsed body.
|
||||
@@ -30,11 +20,7 @@ func MustParseBody(input string) Body {
|
||||
// MustParseBodyWithOpts returns a parsed body.
|
||||
// If an error occurs during parsing, panic.
|
||||
func MustParseBodyWithOpts(input string, opts ParserOptions) Body {
|
||||
parsed, err := ParseBodyWithOpts(input, opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
return v1.MustParseBodyWithOpts(input, setDefaultRegoVersion(opts))
|
||||
}
|
||||
|
||||
// MustParseExpr returns a parsed expression.
|
||||
@@ -66,11 +52,7 @@ func MustParseModule(input string) *Module {
|
||||
// MustParseModuleWithOpts returns a parsed module.
|
||||
// If an error occurs during parsing, panic.
|
||||
func MustParseModuleWithOpts(input string, opts ParserOptions) *Module {
|
||||
parsed, err := ParseModuleWithOpts("", input, opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
return v1.MustParseModuleWithOpts(input, setDefaultRegoVersion(opts))
|
||||
}
|
||||
|
||||
// MustParsePackage returns a Package.
|
||||
@@ -104,11 +86,7 @@ func MustParseStatement(input string) Statement {
|
||||
}
|
||||
|
||||
func MustParseStatementWithOpts(input string, popts ParserOptions) Statement {
|
||||
parsed, err := ParseStatementWithOpts(input, popts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
return v1.MustParseStatementWithOpts(input, setDefaultRegoVersion(popts))
|
||||
}
|
||||
|
||||
// MustParseRef returns a parsed reference.
|
||||
@@ -134,11 +112,7 @@ func MustParseRule(input string) *Rule {
|
||||
// MustParseRuleWithOpts returns a parsed rule.
|
||||
// If an error occurs during parsing, panic.
|
||||
func MustParseRuleWithOpts(input string, opts ParserOptions) *Rule {
|
||||
parsed, err := ParseRuleWithOpts(input, opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
return v1.MustParseRuleWithOpts(input, setDefaultRegoVersion(opts))
|
||||
}
|
||||
|
||||
// MustParseTerm returns a parsed term.
|
||||
@@ -154,331 +128,59 @@ func MustParseTerm(input string) *Term {
|
||||
// ParseRuleFromBody returns a rule if the body can be interpreted as a rule
|
||||
// definition. Otherwise, an error is returned.
|
||||
func ParseRuleFromBody(module *Module, body Body) (*Rule, error) {
|
||||
|
||||
if len(body) != 1 {
|
||||
return nil, fmt.Errorf("multiple expressions cannot be used for rule head")
|
||||
}
|
||||
|
||||
return ParseRuleFromExpr(module, body[0])
|
||||
return v1.ParseRuleFromBody(module, body)
|
||||
}
|
||||
|
||||
// ParseRuleFromExpr returns a rule if the expression can be interpreted as a
|
||||
// rule definition.
|
||||
func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) {
|
||||
|
||||
if len(expr.With) > 0 {
|
||||
return nil, fmt.Errorf("expressions using with keyword cannot be used for rule head")
|
||||
}
|
||||
|
||||
if expr.Negated {
|
||||
return nil, fmt.Errorf("negated expressions cannot be used for rule head")
|
||||
}
|
||||
|
||||
if _, ok := expr.Terms.(*SomeDecl); ok {
|
||||
return nil, errors.New("'some' declarations cannot be used for rule head")
|
||||
}
|
||||
|
||||
if term, ok := expr.Terms.(*Term); ok {
|
||||
switch v := term.Value.(type) {
|
||||
case Ref:
|
||||
if len(v) > 2 { // 2+ dots
|
||||
return ParseCompleteDocRuleWithDotsFromTerm(module, term)
|
||||
}
|
||||
return ParsePartialSetDocRuleFromTerm(module, term)
|
||||
default:
|
||||
return nil, fmt.Errorf("%v cannot be used for rule name", TypeName(v))
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := expr.Terms.([]*Term); !ok {
|
||||
// This is a defensive check in case other kinds of expression terms are
|
||||
// introduced in the future.
|
||||
return nil, errors.New("expression cannot be used for rule head")
|
||||
}
|
||||
|
||||
if expr.IsEquality() {
|
||||
return parseCompleteRuleFromEq(module, expr)
|
||||
} else if expr.IsAssignment() {
|
||||
rule, err := parseCompleteRuleFromEq(module, expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rule.Head.Assign = true
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
if _, ok := BuiltinMap[expr.Operator().String()]; ok {
|
||||
return nil, fmt.Errorf("rule name conflicts with built-in function")
|
||||
}
|
||||
|
||||
return ParseRuleFromCallExpr(module, expr.Terms.([]*Term))
|
||||
}
|
||||
|
||||
func parseCompleteRuleFromEq(module *Module, expr *Expr) (rule *Rule, err error) {
|
||||
|
||||
// ensure the rule location is set to the expr location
|
||||
// the helper functions called below try to set the location based
|
||||
// on the terms they've been provided but that is not as accurate.
|
||||
defer func() {
|
||||
if rule != nil {
|
||||
rule.Location = expr.Location
|
||||
rule.Head.Location = expr.Location
|
||||
}
|
||||
}()
|
||||
|
||||
lhs, rhs := expr.Operand(0), expr.Operand(1)
|
||||
if lhs == nil || rhs == nil {
|
||||
return nil, errors.New("assignment requires two operands")
|
||||
}
|
||||
|
||||
rule, err = ParseRuleFromCallEqExpr(module, lhs, rhs)
|
||||
if err == nil {
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
rule, err = ParsePartialObjectDocRuleFromEqExpr(module, lhs, rhs)
|
||||
if err == nil {
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
return ParseCompleteDocRuleFromEqExpr(module, lhs, rhs)
|
||||
return v1.ParseRuleFromExpr(module, expr)
|
||||
}
|
||||
|
||||
// ParseCompleteDocRuleFromAssignmentExpr returns a rule if the expression can
|
||||
// be interpreted as a complete document definition declared with the assignment
|
||||
// operator.
|
||||
func ParseCompleteDocRuleFromAssignmentExpr(module *Module, lhs, rhs *Term) (*Rule, error) {
|
||||
|
||||
rule, err := ParseCompleteDocRuleFromEqExpr(module, lhs, rhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rule.Head.Assign = true
|
||||
|
||||
return rule, nil
|
||||
return v1.ParseCompleteDocRuleFromAssignmentExpr(module, lhs, rhs)
|
||||
}
|
||||
|
||||
// ParseCompleteDocRuleFromEqExpr returns a rule if the expression can be
|
||||
// interpreted as a complete document definition.
|
||||
func ParseCompleteDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) {
|
||||
var head *Head
|
||||
|
||||
if v, ok := lhs.Value.(Var); ok {
|
||||
// Modify the code to add the location to the head ref
|
||||
// and set the head ref's jsonOptions.
|
||||
head = VarHead(v, lhs.Location, &lhs.jsonOptions)
|
||||
} else if r, ok := lhs.Value.(Ref); ok { // groundness ?
|
||||
if _, ok := r[0].Value.(Var); !ok {
|
||||
return nil, fmt.Errorf("invalid rule head: %v", r)
|
||||
}
|
||||
head = RefHead(r)
|
||||
if len(r) > 1 && !r[len(r)-1].IsGround() {
|
||||
return nil, fmt.Errorf("ref not ground")
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("%v cannot be used for rule name", TypeName(lhs.Value))
|
||||
}
|
||||
head.Value = rhs
|
||||
head.Location = lhs.Location
|
||||
head.setJSONOptions(lhs.jsonOptions)
|
||||
|
||||
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location))
|
||||
setJSONOptions(body, &rhs.jsonOptions)
|
||||
|
||||
return &Rule{
|
||||
Location: lhs.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
jsonOptions: lhs.jsonOptions,
|
||||
generatedBody: true,
|
||||
}, nil
|
||||
return v1.ParseCompleteDocRuleFromEqExpr(module, lhs, rhs)
|
||||
}
|
||||
|
||||
func ParseCompleteDocRuleWithDotsFromTerm(module *Module, term *Term) (*Rule, error) {
|
||||
ref, ok := term.Value.(Ref)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%v cannot be used for rule name", TypeName(term.Value))
|
||||
}
|
||||
|
||||
if _, ok := ref[0].Value.(Var); !ok {
|
||||
return nil, fmt.Errorf("invalid rule head: %v", ref)
|
||||
}
|
||||
head := RefHead(ref, BooleanTerm(true).SetLocation(term.Location))
|
||||
head.generatedValue = true
|
||||
head.Location = term.Location
|
||||
head.jsonOptions = term.jsonOptions
|
||||
|
||||
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(term.Location)).SetLocation(term.Location))
|
||||
setJSONOptions(body, &term.jsonOptions)
|
||||
|
||||
return &Rule{
|
||||
Location: term.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
|
||||
jsonOptions: term.jsonOptions,
|
||||
}, nil
|
||||
return v1.ParseCompleteDocRuleWithDotsFromTerm(module, term)
|
||||
}
|
||||
|
||||
// ParsePartialObjectDocRuleFromEqExpr returns a rule if the expression can be
|
||||
// interpreted as a partial object document definition.
|
||||
func ParsePartialObjectDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) {
|
||||
ref, ok := lhs.Value.(Ref)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%v cannot be used as rule name", TypeName(lhs.Value))
|
||||
}
|
||||
|
||||
if _, ok := ref[0].Value.(Var); !ok {
|
||||
return nil, fmt.Errorf("invalid rule head: %v", ref)
|
||||
}
|
||||
|
||||
head := RefHead(ref, rhs)
|
||||
if len(ref) == 2 { // backcompat for naked `foo.bar = "baz"` statements
|
||||
head.Name = ref[0].Value.(Var)
|
||||
head.Key = ref[1]
|
||||
}
|
||||
head.Location = rhs.Location
|
||||
head.jsonOptions = rhs.jsonOptions
|
||||
|
||||
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location))
|
||||
setJSONOptions(body, &rhs.jsonOptions)
|
||||
|
||||
rule := &Rule{
|
||||
Location: rhs.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
jsonOptions: rhs.jsonOptions,
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
return v1.ParsePartialObjectDocRuleFromEqExpr(module, lhs, rhs)
|
||||
}
|
||||
|
||||
// ParsePartialSetDocRuleFromTerm returns a rule if the term can be interpreted
|
||||
// as a partial set document definition.
|
||||
func ParsePartialSetDocRuleFromTerm(module *Module, term *Term) (*Rule, error) {
|
||||
|
||||
ref, ok := term.Value.(Ref)
|
||||
if !ok || len(ref) == 1 {
|
||||
return nil, fmt.Errorf("%vs cannot be used for rule head", TypeName(term.Value))
|
||||
}
|
||||
if _, ok := ref[0].Value.(Var); !ok {
|
||||
return nil, fmt.Errorf("invalid rule head: %v", ref)
|
||||
}
|
||||
|
||||
head := RefHead(ref)
|
||||
if len(ref) == 2 {
|
||||
v, ok := ref[0].Value.(Var)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%vs cannot be used for rule head", TypeName(term.Value))
|
||||
}
|
||||
// Modify the code to add the location to the head ref
|
||||
// and set the head ref's jsonOptions.
|
||||
head = VarHead(v, ref[0].Location, &ref[0].jsonOptions)
|
||||
head.Key = ref[1]
|
||||
}
|
||||
head.Location = term.Location
|
||||
head.jsonOptions = term.jsonOptions
|
||||
|
||||
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(term.Location)).SetLocation(term.Location))
|
||||
setJSONOptions(body, &term.jsonOptions)
|
||||
|
||||
rule := &Rule{
|
||||
Location: term.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
jsonOptions: term.jsonOptions,
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
return v1.ParsePartialSetDocRuleFromTerm(module, term)
|
||||
}
|
||||
|
||||
// ParseRuleFromCallEqExpr returns a rule if the term can be interpreted as a
|
||||
// function definition (e.g., f(x) = y => f(x) = y { true }).
|
||||
func ParseRuleFromCallEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) {
|
||||
|
||||
call, ok := lhs.Value.(Call)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("must be call")
|
||||
}
|
||||
|
||||
ref, ok := call[0].Value.(Ref)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%vs cannot be used in function signature", TypeName(call[0].Value))
|
||||
}
|
||||
if _, ok := ref[0].Value.(Var); !ok {
|
||||
return nil, fmt.Errorf("invalid rule head: %v", ref)
|
||||
}
|
||||
|
||||
head := RefHead(ref, rhs)
|
||||
head.Location = lhs.Location
|
||||
head.Args = Args(call[1:])
|
||||
head.jsonOptions = lhs.jsonOptions
|
||||
|
||||
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location))
|
||||
setJSONOptions(body, &rhs.jsonOptions)
|
||||
|
||||
rule := &Rule{
|
||||
Location: lhs.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
jsonOptions: lhs.jsonOptions,
|
||||
}
|
||||
|
||||
return rule, nil
|
||||
return v1.ParseRuleFromCallEqExpr(module, lhs, rhs)
|
||||
}
|
||||
|
||||
// ParseRuleFromCallExpr returns a rule if the terms can be interpreted as a
|
||||
// function returning true or some value (e.g., f(x) => f(x) = true { true }).
|
||||
func ParseRuleFromCallExpr(module *Module, terms []*Term) (*Rule, error) {
|
||||
|
||||
if len(terms) <= 1 {
|
||||
return nil, fmt.Errorf("rule argument list must take at least one argument")
|
||||
}
|
||||
|
||||
loc := terms[0].Location
|
||||
ref := terms[0].Value.(Ref)
|
||||
if _, ok := ref[0].Value.(Var); !ok {
|
||||
return nil, fmt.Errorf("invalid rule head: %v", ref)
|
||||
}
|
||||
head := RefHead(ref, BooleanTerm(true).SetLocation(loc))
|
||||
head.Location = loc
|
||||
head.Args = terms[1:]
|
||||
head.jsonOptions = terms[0].jsonOptions
|
||||
|
||||
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(loc)).SetLocation(loc))
|
||||
setJSONOptions(body, &terms[0].jsonOptions)
|
||||
|
||||
rule := &Rule{
|
||||
Location: loc,
|
||||
Head: head,
|
||||
Module: module,
|
||||
Body: body,
|
||||
jsonOptions: terms[0].jsonOptions,
|
||||
}
|
||||
return rule, nil
|
||||
return v1.ParseRuleFromCallExpr(module, terms)
|
||||
}
|
||||
|
||||
// ParseImports returns a slice of Import objects.
|
||||
func ParseImports(input string) ([]*Import, error) {
|
||||
stmts, _, err := ParseStatements("", input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := []*Import{}
|
||||
for _, stmt := range stmts {
|
||||
if imp, ok := stmt.(*Import); ok {
|
||||
result = append(result, imp)
|
||||
} else {
|
||||
return nil, fmt.Errorf("expected import but got %T", stmt)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
return v1.ParseImports(input)
|
||||
}
|
||||
|
||||
// ParseModule returns a parsed Module object.
|
||||
@@ -492,11 +194,7 @@ func ParseModule(filename, input string) (*Module, error) {
|
||||
// For details on Module objects and their fields, see policy.go.
|
||||
// Empty input will return nil, nil.
|
||||
func ParseModuleWithOpts(filename, input string, popts ParserOptions) (*Module, error) {
|
||||
stmts, comments, err := ParseStatementsWithOpts(filename, input, popts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseModule(filename, stmts, comments, popts.RegoVersion)
|
||||
return v1.ParseModuleWithOpts(filename, input, setDefaultRegoVersion(popts))
|
||||
}
|
||||
|
||||
// ParseBody returns exactly one body.
|
||||
@@ -508,28 +206,7 @@ func ParseBody(input string) (Body, error) {
|
||||
// ParseBodyWithOpts returns exactly one body. It does _not_ set SkipRules: true on its own,
|
||||
// but respects whatever ParserOptions it's been given.
|
||||
func ParseBodyWithOpts(input string, popts ParserOptions) (Body, error) {
|
||||
|
||||
stmts, _, err := ParseStatementsWithOpts("", input, popts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := Body{}
|
||||
|
||||
for _, stmt := range stmts {
|
||||
switch stmt := stmt.(type) {
|
||||
case Body:
|
||||
for i := range stmt {
|
||||
result.Append(stmt[i])
|
||||
}
|
||||
case *Comment:
|
||||
// skip
|
||||
default:
|
||||
return nil, fmt.Errorf("expected body but got %T", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return v1.ParseBodyWithOpts(input, setDefaultRegoVersion(popts))
|
||||
}
|
||||
|
||||
// ParseExpr returns exactly one expression.
|
||||
@@ -548,15 +225,7 @@ func ParseExpr(input string) (*Expr, error) {
|
||||
// ParsePackage returns exactly one Package.
|
||||
// If multiple statements are parsed, an error is returned.
|
||||
func ParsePackage(input string) (*Package, error) {
|
||||
stmt, err := ParseStatement(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg, ok := stmt.(*Package)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected package but got %T", stmt)
|
||||
}
|
||||
return pkg, nil
|
||||
return v1.ParsePackage(input)
|
||||
}
|
||||
|
||||
// ParseTerm returns exactly one term.
|
||||
@@ -592,18 +261,7 @@ func ParseRef(input string) (Ref, error) {
|
||||
// ParseRuleWithOpts returns exactly one rule.
|
||||
// If multiple rules are parsed, an error is returned.
|
||||
func ParseRuleWithOpts(input string, opts ParserOptions) (*Rule, error) {
|
||||
stmts, _, err := ParseStatementsWithOpts("", input, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(stmts) != 1 {
|
||||
return nil, fmt.Errorf("expected exactly one statement (rule), got %v = %T, %T", stmts, stmts[0], stmts[1])
|
||||
}
|
||||
rule, ok := stmts[0].(*Rule)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected rule but got %T", stmts[0])
|
||||
}
|
||||
return rule, nil
|
||||
return v1.ParseRuleWithOpts(input, setDefaultRegoVersion(opts))
|
||||
}
|
||||
|
||||
// ParseRule returns exactly one rule.
|
||||
@@ -622,20 +280,13 @@ func ParseStatement(input string) (Statement, error) {
|
||||
return nil, err
|
||||
}
|
||||
if len(stmts) != 1 {
|
||||
return nil, fmt.Errorf("expected exactly one statement")
|
||||
return nil, errors.New("expected exactly one statement")
|
||||
}
|
||||
return stmts[0], nil
|
||||
}
|
||||
|
||||
func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) {
|
||||
stmts, _, err := ParseStatementsWithOpts("", input, popts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(stmts) != 1 {
|
||||
return nil, fmt.Errorf("expected exactly one statement")
|
||||
}
|
||||
return stmts[0], nil
|
||||
return v1.ParseStatementWithOpts(input, setDefaultRegoVersion(popts))
|
||||
}
|
||||
|
||||
// ParseStatements is deprecated. Use ParseStatementWithOpts instead.
|
||||
@@ -646,204 +297,15 @@ func ParseStatements(filename, input string) ([]Statement, []*Comment, error) {
|
||||
// ParseStatementsWithOpts returns a slice of parsed statements. This is the
|
||||
// default return value from the parser.
|
||||
func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Statement, []*Comment, error) {
|
||||
|
||||
parser := NewParser().
|
||||
WithFilename(filename).
|
||||
WithReader(bytes.NewBufferString(input)).
|
||||
WithProcessAnnotation(popts.ProcessAnnotation).
|
||||
WithFutureKeywords(popts.FutureKeywords...).
|
||||
WithAllFutureKeywords(popts.AllFutureKeywords).
|
||||
WithCapabilities(popts.Capabilities).
|
||||
WithSkipRules(popts.SkipRules).
|
||||
WithJSONOptions(popts.JSONOptions).
|
||||
WithRegoVersion(popts.RegoVersion).
|
||||
withUnreleasedKeywords(popts.unreleasedKeywords)
|
||||
|
||||
stmts, comments, errs := parser.Parse()
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
return stmts, comments, nil
|
||||
}
|
||||
|
||||
func parseModule(filename string, stmts []Statement, comments []*Comment, regoCompatibilityMode RegoVersion) (*Module, error) {
|
||||
|
||||
if len(stmts) == 0 {
|
||||
return nil, NewError(ParseErr, &Location{File: filename}, "empty module")
|
||||
}
|
||||
|
||||
var errs Errors
|
||||
|
||||
pkg, ok := stmts[0].(*Package)
|
||||
if !ok {
|
||||
loc := stmts[0].Loc()
|
||||
errs = append(errs, NewError(ParseErr, loc, "package expected"))
|
||||
}
|
||||
|
||||
mod := &Module{
|
||||
Package: pkg,
|
||||
stmts: stmts,
|
||||
}
|
||||
|
||||
// The comments slice only holds comments that were not their own statements.
|
||||
mod.Comments = append(mod.Comments, comments...)
|
||||
mod.regoVersion = regoCompatibilityMode
|
||||
|
||||
for i, stmt := range stmts[1:] {
|
||||
switch stmt := stmt.(type) {
|
||||
case *Import:
|
||||
mod.Imports = append(mod.Imports, stmt)
|
||||
if mod.regoVersion == RegoV0 && Compare(stmt.Path.Value, RegoV1CompatibleRef) == 0 {
|
||||
mod.regoVersion = RegoV0CompatV1
|
||||
}
|
||||
case *Rule:
|
||||
setRuleModule(stmt, mod)
|
||||
mod.Rules = append(mod.Rules, stmt)
|
||||
case Body:
|
||||
rule, err := ParseRuleFromBody(mod, stmt)
|
||||
if err != nil {
|
||||
errs = append(errs, NewError(ParseErr, stmt[0].Location, err.Error()))
|
||||
continue
|
||||
}
|
||||
rule.generatedBody = true
|
||||
mod.Rules = append(mod.Rules, rule)
|
||||
|
||||
// NOTE(tsandall): the statement should now be interpreted as a
|
||||
// rule so update the statement list. This is important for the
|
||||
// logic below that associates annotations with statements.
|
||||
stmts[i+1] = rule
|
||||
case *Package:
|
||||
errs = append(errs, NewError(ParseErr, stmt.Loc(), "unexpected package"))
|
||||
case *Annotations:
|
||||
mod.Annotations = append(mod.Annotations, stmt)
|
||||
case *Comment:
|
||||
// Ignore comments, they're handled above.
|
||||
default:
|
||||
panic("illegal value") // Indicates grammar is out-of-sync with code.
|
||||
}
|
||||
}
|
||||
|
||||
if mod.regoVersion == RegoV0CompatV1 || mod.regoVersion == RegoV1 {
|
||||
for _, rule := range mod.Rules {
|
||||
for r := rule; r != nil; r = r.Else {
|
||||
errs = append(errs, CheckRegoV1(r)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
errs = append(errs, attachAnnotationsNodes(mod)...)
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
attachRuleAnnotations(mod)
|
||||
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func ruleDeclarationHasKeyword(rule *Rule, keyword tokens.Token) bool {
|
||||
for _, kw := range rule.Head.keywords {
|
||||
if kw == keyword {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newScopeAttachmentErr(a *Annotations, want string) *Error {
|
||||
var have string
|
||||
if a.node != nil {
|
||||
have = fmt.Sprintf(" (have %v)", TypeName(a.node))
|
||||
}
|
||||
return NewError(ParseErr, a.Loc(), "annotation scope '%v' must be applied to %v%v", a.Scope, want, have)
|
||||
}
|
||||
|
||||
func setRuleModule(rule *Rule, module *Module) {
|
||||
rule.Module = module
|
||||
if rule.Else != nil {
|
||||
setRuleModule(rule.Else, module)
|
||||
}
|
||||
}
|
||||
|
||||
func setJSONOptions(x interface{}, jsonOptions *astJSON.Options) {
|
||||
vis := NewGenericVisitor(func(x interface{}) bool {
|
||||
if x, ok := x.(customJSON); ok {
|
||||
x.setJSONOptions(*jsonOptions)
|
||||
}
|
||||
return false
|
||||
})
|
||||
vis.Walk(x)
|
||||
return v1.ParseStatementsWithOpts(filename, input, setDefaultRegoVersion(popts))
|
||||
}
|
||||
|
||||
// ParserErrorDetail holds additional details for parser errors.
|
||||
type ParserErrorDetail struct {
|
||||
Line string `json:"line"`
|
||||
Idx int `json:"idx"`
|
||||
}
|
||||
|
||||
func newParserErrorDetail(bs []byte, offset int) *ParserErrorDetail {
|
||||
|
||||
// Find first non-space character at or before offset position.
|
||||
if offset >= len(bs) {
|
||||
offset = len(bs) - 1
|
||||
} else if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
for offset > 0 && unicode.IsSpace(rune(bs[offset])) {
|
||||
offset--
|
||||
}
|
||||
|
||||
// Find beginning of line containing offset.
|
||||
begin := offset
|
||||
|
||||
for begin > 0 && !isNewLineChar(bs[begin]) {
|
||||
begin--
|
||||
}
|
||||
|
||||
if isNewLineChar(bs[begin]) {
|
||||
begin++
|
||||
}
|
||||
|
||||
// Find end of line containing offset.
|
||||
end := offset
|
||||
|
||||
for end < len(bs) && !isNewLineChar(bs[end]) {
|
||||
end++
|
||||
}
|
||||
|
||||
if begin > end {
|
||||
begin = end
|
||||
}
|
||||
|
||||
// Extract line and compute index of offset byte in line.
|
||||
line := bs[begin:end]
|
||||
index := offset - begin
|
||||
|
||||
return &ParserErrorDetail{
|
||||
Line: string(line),
|
||||
Idx: index,
|
||||
}
|
||||
}
|
||||
|
||||
// Lines returns the pretty formatted line output for the error details.
|
||||
func (d ParserErrorDetail) Lines() []string {
|
||||
line := strings.TrimLeft(d.Line, "\t") // remove leading tabs
|
||||
tabCount := len(d.Line) - len(line)
|
||||
indent := d.Idx - tabCount
|
||||
if indent < 0 {
|
||||
indent = 0
|
||||
}
|
||||
return []string{line, strings.Repeat(" ", indent) + "^"}
|
||||
}
|
||||
|
||||
func isNewLineChar(b byte) bool {
|
||||
return b == '\r' || b == '\n'
|
||||
type ParserErrorDetail = v1.ParserErrorDetail
|
||||
|
||||
func setDefaultRegoVersion(opts ParserOptions) ParserOptions {
|
||||
if opts.RegoVersion == RegoUndefined {
|
||||
opts.RegoVersion = DefaultRegoVersion
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
1972
vendor/github.com/open-policy-agent/opa/ast/policy.go
generated
vendored
1972
vendor/github.com/open-policy-agent/opa/ast/policy.go
generated
vendored
File diff suppressed because it is too large
Load Diff
70
vendor/github.com/open-policy-agent/opa/ast/pretty.go
generated
vendored
70
vendor/github.com/open-policy-agent/opa/ast/pretty.go
generated
vendored
@@ -5,78 +5,14 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// Pretty writes a pretty representation of the AST rooted at x to w.
|
||||
//
|
||||
// This is function is intended for debug purposes when inspecting ASTs.
|
||||
func Pretty(w io.Writer, x interface{}) {
|
||||
pp := &prettyPrinter{
|
||||
depth: -1,
|
||||
w: w,
|
||||
}
|
||||
NewBeforeAfterVisitor(pp.Before, pp.After).Walk(x)
|
||||
}
|
||||
|
||||
type prettyPrinter struct {
|
||||
depth int
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (pp *prettyPrinter) Before(x interface{}) bool {
|
||||
switch x.(type) {
|
||||
case *Term:
|
||||
default:
|
||||
pp.depth++
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case *Term:
|
||||
return false
|
||||
case Args:
|
||||
if len(x) == 0 {
|
||||
return false
|
||||
}
|
||||
pp.writeType(x)
|
||||
case *Expr:
|
||||
extras := []string{}
|
||||
if x.Negated {
|
||||
extras = append(extras, "negated")
|
||||
}
|
||||
extras = append(extras, fmt.Sprintf("index=%d", x.Index))
|
||||
pp.writeIndent("%v %v", TypeName(x), strings.Join(extras, " "))
|
||||
case Null, Boolean, Number, String, Var:
|
||||
pp.writeValue(x)
|
||||
default:
|
||||
pp.writeType(x)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (pp *prettyPrinter) After(x interface{}) {
|
||||
switch x.(type) {
|
||||
case *Term:
|
||||
default:
|
||||
pp.depth--
|
||||
}
|
||||
}
|
||||
|
||||
func (pp *prettyPrinter) writeValue(x interface{}) {
|
||||
pp.writeIndent(fmt.Sprint(x))
|
||||
}
|
||||
|
||||
func (pp *prettyPrinter) writeType(x interface{}) {
|
||||
pp.writeIndent(TypeName(x))
|
||||
}
|
||||
|
||||
func (pp *prettyPrinter) writeIndent(f string, a ...interface{}) {
|
||||
pad := strings.Repeat(" ", pp.depth)
|
||||
pp.write(pad+f, a...)
|
||||
}
|
||||
|
||||
func (pp *prettyPrinter) write(f string, a ...interface{}) {
|
||||
fmt.Fprintf(pp.w, f+"\n", a...)
|
||||
v1.Pretty(w, x)
|
||||
}
|
||||
|
||||
208
vendor/github.com/open-policy-agent/opa/ast/rego_v1.go
generated
vendored
208
vendor/github.com/open-policy-agent/opa/ast/rego_v1.go
generated
vendored
@@ -1,208 +0,0 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast/internal/tokens"
|
||||
)
|
||||
|
||||
func checkDuplicateImports(modules []*Module) (errors Errors) {
|
||||
for _, module := range modules {
|
||||
processedImports := map[Var]*Import{}
|
||||
|
||||
for _, imp := range module.Imports {
|
||||
name := imp.Name()
|
||||
|
||||
if processed, conflict := processedImports[name]; conflict {
|
||||
errors = append(errors, NewError(CompileErr, imp.Location, "import must not shadow %v", processed))
|
||||
} else {
|
||||
processedImports[name] = imp
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkRootDocumentOverrides(node interface{}) Errors {
|
||||
errors := Errors{}
|
||||
|
||||
WalkRules(node, func(rule *Rule) bool {
|
||||
var name string
|
||||
if len(rule.Head.Reference) > 0 {
|
||||
name = rule.Head.Reference[0].Value.(Var).String()
|
||||
} else {
|
||||
name = rule.Head.Name.String()
|
||||
}
|
||||
if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) {
|
||||
errors = append(errors, NewError(CompileErr, rule.Location, "rules must not shadow %v (use a different rule name)", name))
|
||||
}
|
||||
|
||||
for _, arg := range rule.Head.Args {
|
||||
if _, ok := arg.Value.(Ref); ok {
|
||||
if RootDocumentRefs.Contains(arg) {
|
||||
errors = append(errors, NewError(CompileErr, arg.Location, "args must not shadow %v (use a different variable name)", arg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
WalkExprs(node, func(expr *Expr) bool {
|
||||
if expr.IsAssignment() {
|
||||
// assign() can be called directly, so we need to assert its given first operand exists before checking its name.
|
||||
if nameOp := expr.Operand(0); nameOp != nil {
|
||||
name := nameOp.String()
|
||||
if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) {
|
||||
errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func walkCalls(node interface{}, f func(interface{}) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
switch x := x.(type) {
|
||||
case Call:
|
||||
return f(x)
|
||||
case *Expr:
|
||||
if x.IsCall() {
|
||||
return f(x)
|
||||
}
|
||||
case *Head:
|
||||
// GenericVisitor doesn't walk the rule head ref
|
||||
walkCalls(x.Reference, f)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(node)
|
||||
}
|
||||
|
||||
func checkDeprecatedBuiltins(deprecatedBuiltinsMap map[string]struct{}, node interface{}) Errors {
|
||||
errs := make(Errors, 0)
|
||||
|
||||
walkCalls(node, func(x interface{}) bool {
|
||||
var operator string
|
||||
var loc *Location
|
||||
|
||||
switch x := x.(type) {
|
||||
case *Expr:
|
||||
operator = x.Operator().String()
|
||||
loc = x.Loc()
|
||||
case Call:
|
||||
terms := []*Term(x)
|
||||
if len(terms) > 0 {
|
||||
operator = terms[0].Value.String()
|
||||
loc = terms[0].Loc()
|
||||
}
|
||||
}
|
||||
|
||||
if operator != "" {
|
||||
if _, ok := deprecatedBuiltinsMap[operator]; ok {
|
||||
errs = append(errs, NewError(TypeErr, loc, "deprecated built-in function calls in expression: %v", operator))
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func checkDeprecatedBuiltinsForCurrentVersion(node interface{}) Errors {
|
||||
deprecatedBuiltins := make(map[string]struct{})
|
||||
capabilities := CapabilitiesForThisVersion()
|
||||
for _, bi := range capabilities.Builtins {
|
||||
if bi.IsDeprecated() {
|
||||
deprecatedBuiltins[bi.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return checkDeprecatedBuiltins(deprecatedBuiltins, node)
|
||||
}
|
||||
|
||||
type RegoCheckOptions struct {
|
||||
NoDuplicateImports bool
|
||||
NoRootDocumentOverrides bool
|
||||
NoDeprecatedBuiltins bool
|
||||
NoKeywordsAsRuleNames bool
|
||||
RequireIfKeyword bool
|
||||
RequireContainsKeyword bool
|
||||
RequireRuleBodyOrValue bool
|
||||
}
|
||||
|
||||
func NewRegoCheckOptions() RegoCheckOptions {
|
||||
// all options are enabled by default
|
||||
return RegoCheckOptions{
|
||||
NoDuplicateImports: true,
|
||||
NoRootDocumentOverrides: true,
|
||||
NoDeprecatedBuiltins: true,
|
||||
NoKeywordsAsRuleNames: true,
|
||||
RequireIfKeyword: true,
|
||||
RequireContainsKeyword: true,
|
||||
RequireRuleBodyOrValue: true,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckRegoV1 checks the given module or rule for errors that are specific to Rego v1.
|
||||
// Passing something other than an *ast.Rule or *ast.Module is considered a programming error, and will cause a panic.
|
||||
func CheckRegoV1(x interface{}) Errors {
|
||||
return CheckRegoV1WithOptions(x, NewRegoCheckOptions())
|
||||
}
|
||||
|
||||
func CheckRegoV1WithOptions(x interface{}, opts RegoCheckOptions) Errors {
|
||||
switch x := x.(type) {
|
||||
case *Module:
|
||||
return checkRegoV1Module(x, opts)
|
||||
case *Rule:
|
||||
return checkRegoV1Rule(x, opts)
|
||||
}
|
||||
panic(fmt.Sprintf("cannot check rego-v1 compatibility on type %T", x))
|
||||
}
|
||||
|
||||
func checkRegoV1Module(module *Module, opts RegoCheckOptions) Errors {
|
||||
var errors Errors
|
||||
if opts.NoDuplicateImports {
|
||||
errors = append(errors, checkDuplicateImports([]*Module{module})...)
|
||||
}
|
||||
if opts.NoRootDocumentOverrides {
|
||||
errors = append(errors, checkRootDocumentOverrides(module)...)
|
||||
}
|
||||
if opts.NoDeprecatedBuiltins {
|
||||
errors = append(errors, checkDeprecatedBuiltinsForCurrentVersion(module)...)
|
||||
}
|
||||
|
||||
for _, rule := range module.Rules {
|
||||
errors = append(errors, checkRegoV1Rule(rule, opts)...)
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func checkRegoV1Rule(rule *Rule, opts RegoCheckOptions) Errors {
|
||||
t := "rule"
|
||||
if rule.isFunction() {
|
||||
t = "function"
|
||||
}
|
||||
|
||||
var errs Errors
|
||||
|
||||
if opts.NoKeywordsAsRuleNames && IsKeywordInRegoVersion(rule.Head.Name.String(), RegoV1) {
|
||||
errs = append(errs, NewError(ParseErr, rule.Location, fmt.Sprintf("%s keyword cannot be used for rule name", rule.Head.Name.String())))
|
||||
}
|
||||
if opts.RequireRuleBodyOrValue && rule.generatedBody && rule.Head.generatedValue {
|
||||
errs = append(errs, NewError(ParseErr, rule.Location, "%s must have value assignment and/or body declaration", t))
|
||||
}
|
||||
if opts.RequireIfKeyword && rule.Body != nil && !rule.generatedBody && !ruleDeclarationHasKeyword(rule, tokens.If) && !rule.Default {
|
||||
errs = append(errs, NewError(ParseErr, rule.Location, "`if` keyword is required before %s body", t))
|
||||
}
|
||||
if opts.RequireContainsKeyword && rule.Head.RuleKind() == MultiValue && !ruleDeclarationHasKeyword(rule, tokens.Contains) {
|
||||
errs = append(errs, NewError(ParseErr, rule.Location, "`contains` keyword is required for partial set rules"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
52
vendor/github.com/open-policy-agent/opa/ast/schema.go
generated
vendored
52
vendor/github.com/open-policy-agent/opa/ast/schema.go
generated
vendored
@@ -5,59 +5,13 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/open-policy-agent/opa/types"
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// SchemaSet holds a map from a path to a schema.
|
||||
type SchemaSet struct {
|
||||
m *util.HashMap
|
||||
}
|
||||
type SchemaSet = v1.SchemaSet
|
||||
|
||||
// NewSchemaSet returns an empty SchemaSet.
|
||||
func NewSchemaSet() *SchemaSet {
|
||||
|
||||
eqFunc := func(a, b util.T) bool {
|
||||
return a.(Ref).Equal(b.(Ref))
|
||||
}
|
||||
|
||||
hashFunc := func(x util.T) int { return x.(Ref).Hash() }
|
||||
|
||||
return &SchemaSet{
|
||||
m: util.NewHashMap(eqFunc, hashFunc),
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts a raw schema into the set.
|
||||
func (ss *SchemaSet) Put(path Ref, raw interface{}) {
|
||||
ss.m.Put(path, raw)
|
||||
}
|
||||
|
||||
// Get returns the raw schema identified by the path.
|
||||
func (ss *SchemaSet) Get(path Ref) interface{} {
|
||||
if ss == nil {
|
||||
return nil
|
||||
}
|
||||
x, ok := ss.m.Get(path)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func loadSchema(raw interface{}, allowNet []string) (types.Type, error) {
|
||||
|
||||
jsonSchema, err := compileSchema(raw, allowNet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tpe, err := newSchemaParser().parseSchema(jsonSchema.RootSchema)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("type checking: %w", err)
|
||||
}
|
||||
|
||||
return tpe, nil
|
||||
return v1.NewSchemaSet()
|
||||
}
|
||||
|
||||
8
vendor/github.com/open-policy-agent/opa/ast/strings.go
generated
vendored
8
vendor/github.com/open-policy-agent/opa/ast/strings.go
generated
vendored
@@ -5,14 +5,10 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// TypeName returns a human readable name for the AST element type.
|
||||
func TypeName(x interface{}) string {
|
||||
if _, ok := x.(*lazyObj); ok {
|
||||
return "object"
|
||||
}
|
||||
return strings.ToLower(reflect.Indirect(reflect.ValueOf(x)).Type().Name())
|
||||
return v1.TypeName(x)
|
||||
}
|
||||
|
||||
3098
vendor/github.com/open-policy-agent/opa/ast/term.go
generated
vendored
3098
vendor/github.com/open-policy-agent/opa/ast/term.go
generated
vendored
File diff suppressed because it is too large
Load Diff
401
vendor/github.com/open-policy-agent/opa/ast/transform.go
generated
vendored
401
vendor/github.com/open-policy-agent/opa/ast/transform.go
generated
vendored
@@ -5,427 +5,42 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// Transformer defines the interface for transforming AST elements. If the
|
||||
// transformer returns nil and does not indicate an error, the AST element will
|
||||
// be set to nil and no transformations will be applied to children of the
|
||||
// element.
|
||||
type Transformer interface {
|
||||
Transform(interface{}) (interface{}, error)
|
||||
}
|
||||
type Transformer = v1.Transformer
|
||||
|
||||
// Transform iterates the AST and calls the Transform function on the
|
||||
// Transformer t for x before recursing.
|
||||
func Transform(t Transformer, x interface{}) (interface{}, error) {
|
||||
|
||||
if term, ok := x.(*Term); ok {
|
||||
return Transform(t, term.Value)
|
||||
}
|
||||
|
||||
y, err := t.Transform(x)
|
||||
if err != nil {
|
||||
return x, err
|
||||
}
|
||||
|
||||
if y == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ok bool
|
||||
switch y := y.(type) {
|
||||
case *Module:
|
||||
p, err := Transform(t, y.Package)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Package, ok = p.(*Package); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Package, p)
|
||||
}
|
||||
for i := range y.Imports {
|
||||
imp, err := Transform(t, y.Imports[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Imports[i], ok = imp.(*Import); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Imports[i], imp)
|
||||
}
|
||||
}
|
||||
for i := range y.Rules {
|
||||
rule, err := Transform(t, y.Rules[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Rules[i], ok = rule.(*Rule); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Rules[i], rule)
|
||||
}
|
||||
}
|
||||
for i := range y.Annotations {
|
||||
a, err := Transform(t, y.Annotations[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Annotations[i], ok = a.(*Annotations); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Annotations[i], a)
|
||||
}
|
||||
}
|
||||
for i := range y.Comments {
|
||||
comment, err := Transform(t, y.Comments[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Comments[i], ok = comment.(*Comment); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Comments[i], comment)
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case *Package:
|
||||
ref, err := Transform(t, y.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Path, ok = ref.(Ref); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Path, ref)
|
||||
}
|
||||
return y, nil
|
||||
case *Import:
|
||||
y.Path, err = transformTerm(t, y.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Alias, err = transformVar(t, y.Alias); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
case *Rule:
|
||||
if y.Head, err = transformHead(t, y.Head); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Body, err = transformBody(t, y.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Else != nil {
|
||||
rule, err := Transform(t, y.Else)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Else, ok = rule.(*Rule); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.Else, rule)
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case *Head:
|
||||
if y.Reference, err = transformRef(t, y.Reference); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Name, err = transformVar(t, y.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Args, err = transformArgs(t, y.Args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Key != nil {
|
||||
if y.Key, err = transformTerm(t, y.Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if y.Value != nil {
|
||||
if y.Value, err = transformTerm(t, y.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case Args:
|
||||
for i := range y {
|
||||
if y[i], err = transformTerm(t, y[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case Body:
|
||||
for i, e := range y {
|
||||
e, err := Transform(t, e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y[i], ok = e.(*Expr); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y[i], e)
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case *Expr:
|
||||
switch ts := y.Terms.(type) {
|
||||
case *SomeDecl:
|
||||
decl, err := Transform(t, ts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Terms, ok = decl.(*SomeDecl); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y, decl)
|
||||
}
|
||||
return y, nil
|
||||
case []*Term:
|
||||
for i := range ts {
|
||||
if ts[i], err = transformTerm(t, ts[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case *Term:
|
||||
if y.Terms, err = transformTerm(t, ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *Every:
|
||||
if ts.Key != nil {
|
||||
ts.Key, err = transformTerm(t, ts.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ts.Value, err = transformTerm(t, ts.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts.Domain, err = transformTerm(t, ts.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts.Body, err = transformBody(t, ts.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y.Terms = ts
|
||||
}
|
||||
for i, w := range y.With {
|
||||
w, err := Transform(t, w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.With[i], ok = w.(*With); !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", y.With[i], w)
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case *With:
|
||||
if y.Target, err = transformTerm(t, y.Target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Value, err = transformTerm(t, y.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
case Ref:
|
||||
for i, term := range y {
|
||||
if y[i], err = transformTerm(t, term); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
case *object:
|
||||
return y.Map(func(k, v *Term) (*Term, *Term, error) {
|
||||
k, err := transformTerm(t, k)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
v, err = transformTerm(t, v)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return k, v, nil
|
||||
})
|
||||
case *Array:
|
||||
for i := 0; i < y.Len(); i++ {
|
||||
v, err := transformTerm(t, y.Elem(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y.set(i, v)
|
||||
}
|
||||
return y, nil
|
||||
case Set:
|
||||
y, err = y.Map(func(term *Term) (*Term, error) {
|
||||
return transformTerm(t, term)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
case *ArrayComprehension:
|
||||
if y.Term, err = transformTerm(t, y.Term); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Body, err = transformBody(t, y.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
case *ObjectComprehension:
|
||||
if y.Key, err = transformTerm(t, y.Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Value, err = transformTerm(t, y.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Body, err = transformBody(t, y.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
case *SetComprehension:
|
||||
if y.Term, err = transformTerm(t, y.Term); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y.Body, err = transformBody(t, y.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
case Call:
|
||||
for i := range y {
|
||||
if y[i], err = transformTerm(t, y[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return y, nil
|
||||
default:
|
||||
return y, nil
|
||||
}
|
||||
return v1.Transform(t, x)
|
||||
}
|
||||
|
||||
// TransformRefs calls the function f on all references under x.
|
||||
func TransformRefs(x interface{}, f func(Ref) (Value, error)) (interface{}, error) {
|
||||
t := &GenericTransformer{func(x interface{}) (interface{}, error) {
|
||||
if r, ok := x.(Ref); ok {
|
||||
return f(r)
|
||||
}
|
||||
return x, nil
|
||||
}}
|
||||
return Transform(t, x)
|
||||
return v1.TransformRefs(x, f)
|
||||
}
|
||||
|
||||
// TransformVars calls the function f on all vars under x.
|
||||
func TransformVars(x interface{}, f func(Var) (Value, error)) (interface{}, error) {
|
||||
t := &GenericTransformer{func(x interface{}) (interface{}, error) {
|
||||
if v, ok := x.(Var); ok {
|
||||
return f(v)
|
||||
}
|
||||
return x, nil
|
||||
}}
|
||||
return Transform(t, x)
|
||||
return v1.TransformVars(x, f)
|
||||
}
|
||||
|
||||
// TransformComprehensions calls the functio nf on all comprehensions under x.
|
||||
func TransformComprehensions(x interface{}, f func(interface{}) (Value, error)) (interface{}, error) {
|
||||
t := &GenericTransformer{func(x interface{}) (interface{}, error) {
|
||||
switch x := x.(type) {
|
||||
case *ArrayComprehension:
|
||||
return f(x)
|
||||
case *SetComprehension:
|
||||
return f(x)
|
||||
case *ObjectComprehension:
|
||||
return f(x)
|
||||
}
|
||||
return x, nil
|
||||
}}
|
||||
return Transform(t, x)
|
||||
return v1.TransformComprehensions(x, f)
|
||||
}
|
||||
|
||||
// GenericTransformer implements the Transformer interface to provide a utility
|
||||
// to transform AST nodes using a closure.
|
||||
type GenericTransformer struct {
|
||||
f func(interface{}) (interface{}, error)
|
||||
}
|
||||
type GenericTransformer = v1.GenericTransformer
|
||||
|
||||
// NewGenericTransformer returns a new GenericTransformer that will transform
|
||||
// AST nodes using the function f.
|
||||
func NewGenericTransformer(f func(x interface{}) (interface{}, error)) *GenericTransformer {
|
||||
return &GenericTransformer{
|
||||
f: f,
|
||||
}
|
||||
}
|
||||
|
||||
// Transform calls the function f on the GenericTransformer.
|
||||
func (t *GenericTransformer) Transform(x interface{}) (interface{}, error) {
|
||||
return t.f(x)
|
||||
}
|
||||
|
||||
func transformHead(t Transformer, head *Head) (*Head, error) {
|
||||
y, err := Transform(t, head)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, ok := y.(*Head)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", head, y)
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func transformArgs(t Transformer, args Args) (Args, error) {
|
||||
y, err := Transform(t, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, ok := y.(Args)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", args, y)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func transformBody(t Transformer, body Body) (Body, error) {
|
||||
y, err := Transform(t, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, ok := y.(Body)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", body, y)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func transformTerm(t Transformer, term *Term) (*Term, error) {
|
||||
v, err := transformValue(t, term.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &Term{
|
||||
Value: v,
|
||||
Location: term.Location,
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func transformValue(t Transformer, v Value) (Value, error) {
|
||||
v1, err := Transform(t, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, ok := v1.(Value)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", v, v1)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func transformVar(t Transformer, v Var) (Var, error) {
|
||||
v1, err := Transform(t, v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r, ok := v1.(Var)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("illegal transform: %T != %T", v, v1)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func transformRef(t Transformer, r Ref) (Ref, error) {
|
||||
r1, err := Transform(t, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r2, ok := r1.(Ref)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("illegal transform: %T != %T", r, r2)
|
||||
}
|
||||
return r2, nil
|
||||
return v1.NewGenericTransformer(f)
|
||||
}
|
||||
|
||||
225
vendor/github.com/open-policy-agent/opa/ast/unify.go
generated
vendored
225
vendor/github.com/open-policy-agent/opa/ast/unify.go
generated
vendored
@@ -4,232 +4,11 @@
|
||||
|
||||
package ast
|
||||
|
||||
func isRefSafe(ref Ref, safe VarSet) bool {
|
||||
switch head := ref[0].Value.(type) {
|
||||
case Var:
|
||||
return safe.Contains(head)
|
||||
case Call:
|
||||
return isCallSafe(head, safe)
|
||||
default:
|
||||
for v := range ref[0].Vars() {
|
||||
if !safe.Contains(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func isCallSafe(call Call, safe VarSet) bool {
|
||||
vis := NewVarVisitor().WithParams(SafetyCheckVisitorParams)
|
||||
vis.Walk(call)
|
||||
unsafe := vis.Vars().Diff(safe)
|
||||
return len(unsafe) == 0
|
||||
}
|
||||
import v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
|
||||
// Unify returns a set of variables that will be unified when the equality expression defined by
|
||||
// terms a and b is evaluated. The unifier assumes that variables in the VarSet safe are already
|
||||
// unified.
|
||||
func Unify(safe VarSet, a *Term, b *Term) VarSet {
|
||||
u := &unifier{
|
||||
safe: safe,
|
||||
unified: VarSet{},
|
||||
unknown: map[Var]VarSet{},
|
||||
}
|
||||
u.unify(a, b)
|
||||
return u.unified
|
||||
}
|
||||
|
||||
type unifier struct {
|
||||
safe VarSet
|
||||
unified VarSet
|
||||
unknown map[Var]VarSet
|
||||
}
|
||||
|
||||
func (u *unifier) isSafe(x Var) bool {
|
||||
return u.safe.Contains(x) || u.unified.Contains(x)
|
||||
}
|
||||
|
||||
func (u *unifier) unify(a *Term, b *Term) {
|
||||
|
||||
switch a := a.Value.(type) {
|
||||
|
||||
case Var:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
if u.isSafe(b) {
|
||||
u.markSafe(a)
|
||||
} else if u.isSafe(a) {
|
||||
u.markSafe(b)
|
||||
} else {
|
||||
u.markUnknown(a, b)
|
||||
u.markUnknown(b, a)
|
||||
}
|
||||
case *Array, Object:
|
||||
u.unifyAll(a, b)
|
||||
case Ref:
|
||||
if isRefSafe(b, u.safe) {
|
||||
u.markSafe(a)
|
||||
}
|
||||
case Call:
|
||||
if isCallSafe(b, u.safe) {
|
||||
u.markSafe(a)
|
||||
}
|
||||
default:
|
||||
u.markSafe(a)
|
||||
}
|
||||
|
||||
case Ref:
|
||||
if isRefSafe(a, u.safe) {
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.markSafe(b)
|
||||
case *Array, Object:
|
||||
u.markAllSafe(b)
|
||||
}
|
||||
}
|
||||
|
||||
case Call:
|
||||
if isCallSafe(a, u.safe) {
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.markSafe(b)
|
||||
case *Array, Object:
|
||||
u.markAllSafe(b)
|
||||
}
|
||||
}
|
||||
|
||||
case *ArrayComprehension:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.markSafe(b)
|
||||
case *Array:
|
||||
u.markAllSafe(b)
|
||||
}
|
||||
case *ObjectComprehension:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.markSafe(b)
|
||||
case *object:
|
||||
u.markAllSafe(b)
|
||||
}
|
||||
case *SetComprehension:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.markSafe(b)
|
||||
}
|
||||
|
||||
case *Array:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.unifyAll(b, a)
|
||||
case *ArrayComprehension, *ObjectComprehension, *SetComprehension:
|
||||
u.markAllSafe(a)
|
||||
case Ref:
|
||||
if isRefSafe(b, u.safe) {
|
||||
u.markAllSafe(a)
|
||||
}
|
||||
case Call:
|
||||
if isCallSafe(b, u.safe) {
|
||||
u.markAllSafe(a)
|
||||
}
|
||||
case *Array:
|
||||
if a.Len() == b.Len() {
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
u.unify(a.Elem(i), b.Elem(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *object:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.unifyAll(b, a)
|
||||
case Ref:
|
||||
if isRefSafe(b, u.safe) {
|
||||
u.markAllSafe(a)
|
||||
}
|
||||
case Call:
|
||||
if isCallSafe(b, u.safe) {
|
||||
u.markAllSafe(a)
|
||||
}
|
||||
case *object:
|
||||
if a.Len() == b.Len() {
|
||||
_ = a.Iter(func(k, v *Term) error {
|
||||
if v2 := b.Get(k); v2 != nil {
|
||||
u.unify(v, v2)
|
||||
}
|
||||
return nil
|
||||
}) // impossible to return error
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
switch b := b.Value.(type) {
|
||||
case Var:
|
||||
u.markSafe(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unifier) markAllSafe(x Value) {
|
||||
vis := u.varVisitor()
|
||||
vis.Walk(x)
|
||||
for v := range vis.Vars() {
|
||||
u.markSafe(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unifier) markSafe(x Var) {
|
||||
u.unified.Add(x)
|
||||
|
||||
// Add dependencies of 'x' to safe set
|
||||
vs := u.unknown[x]
|
||||
delete(u.unknown, x)
|
||||
for v := range vs {
|
||||
u.markSafe(v)
|
||||
}
|
||||
|
||||
// Add dependants of 'x' to safe set if they have no more
|
||||
// dependencies.
|
||||
for v, deps := range u.unknown {
|
||||
if deps.Contains(x) {
|
||||
delete(deps, x)
|
||||
if len(deps) == 0 {
|
||||
u.markSafe(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unifier) markUnknown(a, b Var) {
|
||||
if _, ok := u.unknown[a]; !ok {
|
||||
u.unknown[a] = NewVarSet()
|
||||
}
|
||||
u.unknown[a].Add(b)
|
||||
}
|
||||
|
||||
func (u *unifier) unifyAll(a Var, b Value) {
|
||||
if u.isSafe(a) {
|
||||
u.markAllSafe(b)
|
||||
} else {
|
||||
vis := u.varVisitor()
|
||||
vis.Walk(b)
|
||||
unsafe := vis.Vars().Diff(u.safe).Diff(u.unified)
|
||||
if len(unsafe) == 0 {
|
||||
u.markSafe(a)
|
||||
} else {
|
||||
for v := range unsafe {
|
||||
u.markUnknown(a, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unifier) varVisitor() *VarVisitor {
|
||||
return NewVarVisitor().WithParams(VarVisitorParams{
|
||||
SkipRefHead: true,
|
||||
SkipObjectKeys: true,
|
||||
SkipClosures: true,
|
||||
})
|
||||
return v1.Unify(safe, a, b)
|
||||
}
|
||||
|
||||
89
vendor/github.com/open-policy-agent/opa/ast/varset.go
generated
vendored
89
vendor/github.com/open-policy-agent/opa/ast/varset.go
generated
vendored
@@ -5,96 +5,13 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
// VarSet represents a set of variables.
|
||||
type VarSet map[Var]struct{}
|
||||
type VarSet = v1.VarSet
|
||||
|
||||
// NewVarSet returns a new VarSet containing the specified variables.
|
||||
func NewVarSet(vs ...Var) VarSet {
|
||||
s := VarSet{}
|
||||
for _, v := range vs {
|
||||
s.Add(v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Add updates the set to include the variable "v".
|
||||
func (s VarSet) Add(v Var) {
|
||||
s[v] = struct{}{}
|
||||
}
|
||||
|
||||
// Contains returns true if the set contains the variable "v".
|
||||
func (s VarSet) Contains(v Var) bool {
|
||||
_, ok := s[v]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Copy returns a shallow copy of the VarSet.
|
||||
func (s VarSet) Copy() VarSet {
|
||||
cpy := VarSet{}
|
||||
for v := range s {
|
||||
cpy.Add(v)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// Diff returns a VarSet containing variables in s that are not in vs.
|
||||
func (s VarSet) Diff(vs VarSet) VarSet {
|
||||
r := VarSet{}
|
||||
for v := range s {
|
||||
if !vs.Contains(v) {
|
||||
r.Add(v)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Equal returns true if s contains exactly the same elements as vs.
|
||||
func (s VarSet) Equal(vs VarSet) bool {
|
||||
if len(s.Diff(vs)) > 0 {
|
||||
return false
|
||||
}
|
||||
return len(vs.Diff(s)) == 0
|
||||
}
|
||||
|
||||
// Intersect returns a VarSet containing variables in s that are in vs.
|
||||
func (s VarSet) Intersect(vs VarSet) VarSet {
|
||||
r := VarSet{}
|
||||
for v := range s {
|
||||
if vs.Contains(v) {
|
||||
r.Add(v)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Sorted returns a sorted slice of vars from s.
|
||||
func (s VarSet) Sorted() []Var {
|
||||
sorted := make([]Var, 0, len(s))
|
||||
for v := range s {
|
||||
sorted = append(sorted, v)
|
||||
}
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i].Compare(sorted[j]) < 0
|
||||
})
|
||||
return sorted
|
||||
}
|
||||
|
||||
// Update merges the other VarSet into this VarSet.
|
||||
func (s VarSet) Update(vs VarSet) {
|
||||
for v := range vs {
|
||||
s.Add(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (s VarSet) String() string {
|
||||
tmp := make([]string, 0, len(s))
|
||||
for v := range s {
|
||||
tmp = append(tmp, string(v))
|
||||
}
|
||||
sort.Strings(tmp)
|
||||
return fmt.Sprintf("%v", tmp)
|
||||
return v1.NewVarSet(vs...)
|
||||
}
|
||||
|
||||
1450
vendor/github.com/open-policy-agent/opa/ast/version_index.json
generated
vendored
1450
vendor/github.com/open-policy-agent/opa/ast/version_index.json
generated
vendored
File diff suppressed because it is too large
Load Diff
704
vendor/github.com/open-policy-agent/opa/ast/visit.go
generated
vendored
704
vendor/github.com/open-policy-agent/opa/ast/visit.go
generated
vendored
@@ -4,780 +4,120 @@
|
||||
|
||||
package ast
|
||||
|
||||
import v1 "github.com/open-policy-agent/opa/v1/ast"
|
||||
|
||||
// Visitor defines the interface for iterating AST elements. The Visit function
|
||||
// can return a Visitor w which will be used to visit the children of the AST
|
||||
// element v. If the Visit function returns nil, the children will not be
|
||||
// visited.
|
||||
// Deprecated: use GenericVisitor or another visitor implementation
|
||||
type Visitor interface {
|
||||
Visit(v interface{}) (w Visitor)
|
||||
}
|
||||
type Visitor = v1.Visitor
|
||||
|
||||
// BeforeAndAfterVisitor wraps Visitor to provide hooks for being called before
|
||||
// and after the AST has been visited.
|
||||
// Deprecated: use GenericVisitor or another visitor implementation
|
||||
type BeforeAndAfterVisitor interface {
|
||||
Visitor
|
||||
Before(x interface{})
|
||||
After(x interface{})
|
||||
}
|
||||
type BeforeAndAfterVisitor = v1.BeforeAndAfterVisitor
|
||||
|
||||
// Walk iterates the AST by calling the Visit function on the Visitor
|
||||
// v for x before recursing.
|
||||
// Deprecated: use GenericVisitor.Walk
|
||||
func Walk(v Visitor, x interface{}) {
|
||||
if bav, ok := v.(BeforeAndAfterVisitor); !ok {
|
||||
walk(v, x)
|
||||
} else {
|
||||
bav.Before(x)
|
||||
defer bav.After(x)
|
||||
walk(bav, x)
|
||||
}
|
||||
v1.Walk(v, x)
|
||||
}
|
||||
|
||||
// WalkBeforeAndAfter iterates the AST by calling the Visit function on the
|
||||
// Visitor v for x before recursing.
|
||||
// Deprecated: use GenericVisitor.Walk
|
||||
func WalkBeforeAndAfter(v BeforeAndAfterVisitor, x interface{}) {
|
||||
Walk(v, x)
|
||||
}
|
||||
|
||||
func walk(v Visitor, x interface{}) {
|
||||
w := v.Visit(x)
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
switch x := x.(type) {
|
||||
case *Module:
|
||||
Walk(w, x.Package)
|
||||
for i := range x.Imports {
|
||||
Walk(w, x.Imports[i])
|
||||
}
|
||||
for i := range x.Rules {
|
||||
Walk(w, x.Rules[i])
|
||||
}
|
||||
for i := range x.Annotations {
|
||||
Walk(w, x.Annotations[i])
|
||||
}
|
||||
for i := range x.Comments {
|
||||
Walk(w, x.Comments[i])
|
||||
}
|
||||
case *Package:
|
||||
Walk(w, x.Path)
|
||||
case *Import:
|
||||
Walk(w, x.Path)
|
||||
Walk(w, x.Alias)
|
||||
case *Rule:
|
||||
Walk(w, x.Head)
|
||||
Walk(w, x.Body)
|
||||
if x.Else != nil {
|
||||
Walk(w, x.Else)
|
||||
}
|
||||
case *Head:
|
||||
Walk(w, x.Name)
|
||||
Walk(w, x.Args)
|
||||
if x.Key != nil {
|
||||
Walk(w, x.Key)
|
||||
}
|
||||
if x.Value != nil {
|
||||
Walk(w, x.Value)
|
||||
}
|
||||
case Body:
|
||||
for i := range x {
|
||||
Walk(w, x[i])
|
||||
}
|
||||
case Args:
|
||||
for i := range x {
|
||||
Walk(w, x[i])
|
||||
}
|
||||
case *Expr:
|
||||
switch ts := x.Terms.(type) {
|
||||
case *Term, *SomeDecl, *Every:
|
||||
Walk(w, ts)
|
||||
case []*Term:
|
||||
for i := range ts {
|
||||
Walk(w, ts[i])
|
||||
}
|
||||
}
|
||||
for i := range x.With {
|
||||
Walk(w, x.With[i])
|
||||
}
|
||||
case *With:
|
||||
Walk(w, x.Target)
|
||||
Walk(w, x.Value)
|
||||
case *Term:
|
||||
Walk(w, x.Value)
|
||||
case Ref:
|
||||
for i := range x {
|
||||
Walk(w, x[i])
|
||||
}
|
||||
case *object:
|
||||
x.Foreach(func(k, vv *Term) {
|
||||
Walk(w, k)
|
||||
Walk(w, vv)
|
||||
})
|
||||
case *Array:
|
||||
x.Foreach(func(t *Term) {
|
||||
Walk(w, t)
|
||||
})
|
||||
case Set:
|
||||
x.Foreach(func(t *Term) {
|
||||
Walk(w, t)
|
||||
})
|
||||
case *ArrayComprehension:
|
||||
Walk(w, x.Term)
|
||||
Walk(w, x.Body)
|
||||
case *ObjectComprehension:
|
||||
Walk(w, x.Key)
|
||||
Walk(w, x.Value)
|
||||
Walk(w, x.Body)
|
||||
case *SetComprehension:
|
||||
Walk(w, x.Term)
|
||||
Walk(w, x.Body)
|
||||
case Call:
|
||||
for i := range x {
|
||||
Walk(w, x[i])
|
||||
}
|
||||
case *Every:
|
||||
if x.Key != nil {
|
||||
Walk(w, x.Key)
|
||||
}
|
||||
Walk(w, x.Value)
|
||||
Walk(w, x.Domain)
|
||||
Walk(w, x.Body)
|
||||
case *SomeDecl:
|
||||
for i := range x.Symbols {
|
||||
Walk(w, x.Symbols[i])
|
||||
}
|
||||
}
|
||||
v1.WalkBeforeAndAfter(v, x)
|
||||
}
|
||||
|
||||
// WalkVars calls the function f on all vars under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkVars(x interface{}, f func(Var) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if v, ok := x.(Var); ok {
|
||||
return f(v)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkVars(x, f)
|
||||
}
|
||||
|
||||
// WalkClosures calls the function f on all closures under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkClosures(x interface{}, f func(interface{}) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
switch x := x.(type) {
|
||||
case *ArrayComprehension, *ObjectComprehension, *SetComprehension, *Every:
|
||||
return f(x)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkClosures(x, f)
|
||||
}
|
||||
|
||||
// WalkRefs calls the function f on all references under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkRefs(x interface{}, f func(Ref) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if r, ok := x.(Ref); ok {
|
||||
return f(r)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkRefs(x, f)
|
||||
}
|
||||
|
||||
// WalkTerms calls the function f on all terms under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkTerms(x interface{}, f func(*Term) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if term, ok := x.(*Term); ok {
|
||||
return f(term)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkTerms(x, f)
|
||||
}
|
||||
|
||||
// WalkWiths calls the function f on all with modifiers under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkWiths(x interface{}, f func(*With) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if w, ok := x.(*With); ok {
|
||||
return f(w)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkWiths(x, f)
|
||||
}
|
||||
|
||||
// WalkExprs calls the function f on all expressions under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkExprs(x interface{}, f func(*Expr) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if r, ok := x.(*Expr); ok {
|
||||
return f(r)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkExprs(x, f)
|
||||
}
|
||||
|
||||
// WalkBodies calls the function f on all bodies under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkBodies(x interface{}, f func(Body) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if b, ok := x.(Body); ok {
|
||||
return f(b)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkBodies(x, f)
|
||||
}
|
||||
|
||||
// WalkRules calls the function f on all rules under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkRules(x interface{}, f func(*Rule) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if r, ok := x.(*Rule); ok {
|
||||
stop := f(r)
|
||||
// NOTE(tsandall): since rules cannot be embedded inside of queries
|
||||
// we can stop early if there is no else block.
|
||||
if stop || r.Else == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkRules(x, f)
|
||||
}
|
||||
|
||||
// WalkNodes calls the function f on all nodes under x. If the function f
|
||||
// returns true, AST nodes under the last node will not be visited.
|
||||
func WalkNodes(x interface{}, f func(Node) bool) {
|
||||
vis := &GenericVisitor{func(x interface{}) bool {
|
||||
if n, ok := x.(Node); ok {
|
||||
return f(n)
|
||||
}
|
||||
return false
|
||||
}}
|
||||
vis.Walk(x)
|
||||
v1.WalkNodes(x, f)
|
||||
}
|
||||
|
||||
// GenericVisitor provides a utility to walk over AST nodes using a
|
||||
// closure. If the closure returns true, the visitor will not walk
|
||||
// over AST nodes under x.
|
||||
type GenericVisitor struct {
|
||||
f func(x interface{}) bool
|
||||
}
|
||||
type GenericVisitor = v1.GenericVisitor
|
||||
|
||||
// NewGenericVisitor returns a new GenericVisitor that will invoke the function
|
||||
// f on AST nodes.
|
||||
func NewGenericVisitor(f func(x interface{}) bool) *GenericVisitor {
|
||||
return &GenericVisitor{f}
|
||||
}
|
||||
|
||||
// Walk iterates the AST by calling the function f on the
|
||||
// GenericVisitor before recursing. Contrary to the generic Walk, this
|
||||
// does not require allocating the visitor from heap.
|
||||
func (vis *GenericVisitor) Walk(x interface{}) {
|
||||
if vis.f(x) {
|
||||
return
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case *Module:
|
||||
vis.Walk(x.Package)
|
||||
for i := range x.Imports {
|
||||
vis.Walk(x.Imports[i])
|
||||
}
|
||||
for i := range x.Rules {
|
||||
vis.Walk(x.Rules[i])
|
||||
}
|
||||
for i := range x.Annotations {
|
||||
vis.Walk(x.Annotations[i])
|
||||
}
|
||||
for i := range x.Comments {
|
||||
vis.Walk(x.Comments[i])
|
||||
}
|
||||
case *Package:
|
||||
vis.Walk(x.Path)
|
||||
case *Import:
|
||||
vis.Walk(x.Path)
|
||||
vis.Walk(x.Alias)
|
||||
case *Rule:
|
||||
vis.Walk(x.Head)
|
||||
vis.Walk(x.Body)
|
||||
if x.Else != nil {
|
||||
vis.Walk(x.Else)
|
||||
}
|
||||
case *Head:
|
||||
vis.Walk(x.Name)
|
||||
vis.Walk(x.Args)
|
||||
if x.Key != nil {
|
||||
vis.Walk(x.Key)
|
||||
}
|
||||
if x.Value != nil {
|
||||
vis.Walk(x.Value)
|
||||
}
|
||||
case Body:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case Args:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *Expr:
|
||||
switch ts := x.Terms.(type) {
|
||||
case *Term, *SomeDecl, *Every:
|
||||
vis.Walk(ts)
|
||||
case []*Term:
|
||||
for i := range ts {
|
||||
vis.Walk(ts[i])
|
||||
}
|
||||
}
|
||||
for i := range x.With {
|
||||
vis.Walk(x.With[i])
|
||||
}
|
||||
case *With:
|
||||
vis.Walk(x.Target)
|
||||
vis.Walk(x.Value)
|
||||
case *Term:
|
||||
vis.Walk(x.Value)
|
||||
case Ref:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *object:
|
||||
x.Foreach(func(k, _ *Term) {
|
||||
vis.Walk(k)
|
||||
vis.Walk(x.Get(k))
|
||||
})
|
||||
case Object:
|
||||
x.Foreach(func(k, _ *Term) {
|
||||
vis.Walk(k)
|
||||
vis.Walk(x.Get(k))
|
||||
})
|
||||
case *Array:
|
||||
x.Foreach(func(t *Term) {
|
||||
vis.Walk(t)
|
||||
})
|
||||
case Set:
|
||||
xSlice := x.Slice()
|
||||
for i := range xSlice {
|
||||
vis.Walk(xSlice[i])
|
||||
}
|
||||
case *ArrayComprehension:
|
||||
vis.Walk(x.Term)
|
||||
vis.Walk(x.Body)
|
||||
case *ObjectComprehension:
|
||||
vis.Walk(x.Key)
|
||||
vis.Walk(x.Value)
|
||||
vis.Walk(x.Body)
|
||||
case *SetComprehension:
|
||||
vis.Walk(x.Term)
|
||||
vis.Walk(x.Body)
|
||||
case Call:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *Every:
|
||||
if x.Key != nil {
|
||||
vis.Walk(x.Key)
|
||||
}
|
||||
vis.Walk(x.Value)
|
||||
vis.Walk(x.Domain)
|
||||
vis.Walk(x.Body)
|
||||
case *SomeDecl:
|
||||
for i := range x.Symbols {
|
||||
vis.Walk(x.Symbols[i])
|
||||
}
|
||||
}
|
||||
return v1.NewGenericVisitor(f)
|
||||
}
|
||||
|
||||
// BeforeAfterVisitor provides a utility to walk over AST nodes using
|
||||
// closures. If the before closure returns true, the visitor will not
|
||||
// walk over AST nodes under x. The after closure is invoked always
|
||||
// after visiting a node.
|
||||
type BeforeAfterVisitor struct {
|
||||
before func(x interface{}) bool
|
||||
after func(x interface{})
|
||||
}
|
||||
type BeforeAfterVisitor = v1.BeforeAfterVisitor
|
||||
|
||||
// NewBeforeAfterVisitor returns a new BeforeAndAfterVisitor that
|
||||
// will invoke the functions before and after AST nodes.
|
||||
func NewBeforeAfterVisitor(before func(x interface{}) bool, after func(x interface{})) *BeforeAfterVisitor {
|
||||
return &BeforeAfterVisitor{before, after}
|
||||
}
|
||||
|
||||
// Walk iterates the AST by calling the functions on the
|
||||
// BeforeAndAfterVisitor before and after recursing. Contrary to the
|
||||
// generic Walk, this does not require allocating the visitor from
|
||||
// heap.
|
||||
func (vis *BeforeAfterVisitor) Walk(x interface{}) {
|
||||
defer vis.after(x)
|
||||
if vis.before(x) {
|
||||
return
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case *Module:
|
||||
vis.Walk(x.Package)
|
||||
for i := range x.Imports {
|
||||
vis.Walk(x.Imports[i])
|
||||
}
|
||||
for i := range x.Rules {
|
||||
vis.Walk(x.Rules[i])
|
||||
}
|
||||
for i := range x.Annotations {
|
||||
vis.Walk(x.Annotations[i])
|
||||
}
|
||||
for i := range x.Comments {
|
||||
vis.Walk(x.Comments[i])
|
||||
}
|
||||
case *Package:
|
||||
vis.Walk(x.Path)
|
||||
case *Import:
|
||||
vis.Walk(x.Path)
|
||||
vis.Walk(x.Alias)
|
||||
case *Rule:
|
||||
vis.Walk(x.Head)
|
||||
vis.Walk(x.Body)
|
||||
if x.Else != nil {
|
||||
vis.Walk(x.Else)
|
||||
}
|
||||
case *Head:
|
||||
if len(x.Reference) > 0 {
|
||||
vis.Walk(x.Reference)
|
||||
} else {
|
||||
vis.Walk(x.Name)
|
||||
if x.Key != nil {
|
||||
vis.Walk(x.Key)
|
||||
}
|
||||
}
|
||||
vis.Walk(x.Args)
|
||||
if x.Value != nil {
|
||||
vis.Walk(x.Value)
|
||||
}
|
||||
case Body:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case Args:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *Expr:
|
||||
switch ts := x.Terms.(type) {
|
||||
case *Term, *SomeDecl, *Every:
|
||||
vis.Walk(ts)
|
||||
case []*Term:
|
||||
for i := range ts {
|
||||
vis.Walk(ts[i])
|
||||
}
|
||||
}
|
||||
for i := range x.With {
|
||||
vis.Walk(x.With[i])
|
||||
}
|
||||
case *With:
|
||||
vis.Walk(x.Target)
|
||||
vis.Walk(x.Value)
|
||||
case *Term:
|
||||
vis.Walk(x.Value)
|
||||
case Ref:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *object:
|
||||
x.Foreach(func(k, _ *Term) {
|
||||
vis.Walk(k)
|
||||
vis.Walk(x.Get(k))
|
||||
})
|
||||
case Object:
|
||||
x.Foreach(func(k, _ *Term) {
|
||||
vis.Walk(k)
|
||||
vis.Walk(x.Get(k))
|
||||
})
|
||||
case *Array:
|
||||
x.Foreach(func(t *Term) {
|
||||
vis.Walk(t)
|
||||
})
|
||||
case Set:
|
||||
xSlice := x.Slice()
|
||||
for i := range xSlice {
|
||||
vis.Walk(xSlice[i])
|
||||
}
|
||||
case *ArrayComprehension:
|
||||
vis.Walk(x.Term)
|
||||
vis.Walk(x.Body)
|
||||
case *ObjectComprehension:
|
||||
vis.Walk(x.Key)
|
||||
vis.Walk(x.Value)
|
||||
vis.Walk(x.Body)
|
||||
case *SetComprehension:
|
||||
vis.Walk(x.Term)
|
||||
vis.Walk(x.Body)
|
||||
case Call:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *Every:
|
||||
if x.Key != nil {
|
||||
vis.Walk(x.Key)
|
||||
}
|
||||
vis.Walk(x.Value)
|
||||
vis.Walk(x.Domain)
|
||||
vis.Walk(x.Body)
|
||||
case *SomeDecl:
|
||||
for i := range x.Symbols {
|
||||
vis.Walk(x.Symbols[i])
|
||||
}
|
||||
}
|
||||
return v1.NewBeforeAfterVisitor(before, after)
|
||||
}
|
||||
|
||||
// VarVisitor walks AST nodes under a given node and collects all encountered
|
||||
// variables. The collected variables can be controlled by specifying
|
||||
// VarVisitorParams when creating the visitor.
|
||||
type VarVisitor struct {
|
||||
params VarVisitorParams
|
||||
vars VarSet
|
||||
}
|
||||
type VarVisitor = v1.VarVisitor
|
||||
|
||||
// VarVisitorParams contains settings for a VarVisitor.
|
||||
type VarVisitorParams struct {
|
||||
SkipRefHead bool
|
||||
SkipRefCallHead bool
|
||||
SkipObjectKeys bool
|
||||
SkipClosures bool
|
||||
SkipWithTarget bool
|
||||
SkipSets bool
|
||||
}
|
||||
type VarVisitorParams = v1.VarVisitorParams
|
||||
|
||||
// NewVarVisitor returns a new VarVisitor object.
|
||||
func NewVarVisitor() *VarVisitor {
|
||||
return &VarVisitor{
|
||||
vars: NewVarSet(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithParams sets the parameters in params on vis.
|
||||
func (vis *VarVisitor) WithParams(params VarVisitorParams) *VarVisitor {
|
||||
vis.params = params
|
||||
return vis
|
||||
}
|
||||
|
||||
// Vars returns a VarSet that contains collected vars.
|
||||
func (vis *VarVisitor) Vars() VarSet {
|
||||
return vis.vars
|
||||
}
|
||||
|
||||
// visit determines if the VarVisitor will recurse into x: if it returns `true`,
|
||||
// the visitor will _skip_ that branch of the AST
|
||||
func (vis *VarVisitor) visit(v interface{}) bool {
|
||||
if vis.params.SkipObjectKeys {
|
||||
if o, ok := v.(Object); ok {
|
||||
o.Foreach(func(_, v *Term) {
|
||||
vis.Walk(v)
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
if vis.params.SkipRefHead {
|
||||
if r, ok := v.(Ref); ok {
|
||||
rSlice := r[1:]
|
||||
for i := range rSlice {
|
||||
vis.Walk(rSlice[i])
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if vis.params.SkipClosures {
|
||||
switch v := v.(type) {
|
||||
case *ArrayComprehension, *ObjectComprehension, *SetComprehension:
|
||||
return true
|
||||
case *Expr:
|
||||
if ev, ok := v.Terms.(*Every); ok {
|
||||
vis.Walk(ev.Domain)
|
||||
// We're _not_ walking ev.Body -- that's the closure here
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if vis.params.SkipWithTarget {
|
||||
if v, ok := v.(*With); ok {
|
||||
vis.Walk(v.Value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
if vis.params.SkipSets {
|
||||
if _, ok := v.(Set); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if vis.params.SkipRefCallHead {
|
||||
switch v := v.(type) {
|
||||
case *Expr:
|
||||
if terms, ok := v.Terms.([]*Term); ok {
|
||||
termSlice := terms[0].Value.(Ref)[1:]
|
||||
for i := range termSlice {
|
||||
vis.Walk(termSlice[i])
|
||||
}
|
||||
for i := 1; i < len(terms); i++ {
|
||||
vis.Walk(terms[i])
|
||||
}
|
||||
for i := range v.With {
|
||||
vis.Walk(v.With[i])
|
||||
}
|
||||
return true
|
||||
}
|
||||
case Call:
|
||||
operator := v[0].Value.(Ref)
|
||||
for i := 1; i < len(operator); i++ {
|
||||
vis.Walk(operator[i])
|
||||
}
|
||||
for i := 1; i < len(v); i++ {
|
||||
vis.Walk(v[i])
|
||||
}
|
||||
return true
|
||||
case *With:
|
||||
if ref, ok := v.Target.Value.(Ref); ok {
|
||||
refSlice := ref[1:]
|
||||
for i := range refSlice {
|
||||
vis.Walk(refSlice[i])
|
||||
}
|
||||
}
|
||||
if ref, ok := v.Value.Value.(Ref); ok {
|
||||
refSlice := ref[1:]
|
||||
for i := range refSlice {
|
||||
vis.Walk(refSlice[i])
|
||||
}
|
||||
} else {
|
||||
vis.Walk(v.Value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if v, ok := v.(Var); ok {
|
||||
vis.vars.Add(v)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Walk iterates the AST by calling the function f on the
|
||||
// GenericVisitor before recursing. Contrary to the generic Walk, this
|
||||
// does not require allocating the visitor from heap.
|
||||
func (vis *VarVisitor) Walk(x interface{}) {
|
||||
if vis.visit(x) {
|
||||
return
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case *Module:
|
||||
vis.Walk(x.Package)
|
||||
for i := range x.Imports {
|
||||
vis.Walk(x.Imports[i])
|
||||
}
|
||||
for i := range x.Rules {
|
||||
vis.Walk(x.Rules[i])
|
||||
}
|
||||
for i := range x.Comments {
|
||||
vis.Walk(x.Comments[i])
|
||||
}
|
||||
case *Package:
|
||||
vis.Walk(x.Path)
|
||||
case *Import:
|
||||
vis.Walk(x.Path)
|
||||
vis.Walk(x.Alias)
|
||||
case *Rule:
|
||||
vis.Walk(x.Head)
|
||||
vis.Walk(x.Body)
|
||||
if x.Else != nil {
|
||||
vis.Walk(x.Else)
|
||||
}
|
||||
case *Head:
|
||||
if len(x.Reference) > 0 {
|
||||
vis.Walk(x.Reference)
|
||||
} else {
|
||||
vis.Walk(x.Name)
|
||||
if x.Key != nil {
|
||||
vis.Walk(x.Key)
|
||||
}
|
||||
}
|
||||
vis.Walk(x.Args)
|
||||
|
||||
if x.Value != nil {
|
||||
vis.Walk(x.Value)
|
||||
}
|
||||
case Body:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case Args:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *Expr:
|
||||
switch ts := x.Terms.(type) {
|
||||
case *Term, *SomeDecl, *Every:
|
||||
vis.Walk(ts)
|
||||
case []*Term:
|
||||
for i := range ts {
|
||||
vis.Walk(ts[i])
|
||||
}
|
||||
}
|
||||
for i := range x.With {
|
||||
vis.Walk(x.With[i])
|
||||
}
|
||||
case *With:
|
||||
vis.Walk(x.Target)
|
||||
vis.Walk(x.Value)
|
||||
case *Term:
|
||||
vis.Walk(x.Value)
|
||||
case Ref:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *object:
|
||||
x.Foreach(func(k, _ *Term) {
|
||||
vis.Walk(k)
|
||||
vis.Walk(x.Get(k))
|
||||
})
|
||||
case *Array:
|
||||
x.Foreach(func(t *Term) {
|
||||
vis.Walk(t)
|
||||
})
|
||||
case Set:
|
||||
xSlice := x.Slice()
|
||||
for i := range xSlice {
|
||||
vis.Walk(xSlice[i])
|
||||
}
|
||||
case *ArrayComprehension:
|
||||
vis.Walk(x.Term)
|
||||
vis.Walk(x.Body)
|
||||
case *ObjectComprehension:
|
||||
vis.Walk(x.Key)
|
||||
vis.Walk(x.Value)
|
||||
vis.Walk(x.Body)
|
||||
case *SetComprehension:
|
||||
vis.Walk(x.Term)
|
||||
vis.Walk(x.Body)
|
||||
case Call:
|
||||
for i := range x {
|
||||
vis.Walk(x[i])
|
||||
}
|
||||
case *Every:
|
||||
if x.Key != nil {
|
||||
vis.Walk(x.Key)
|
||||
}
|
||||
vis.Walk(x.Value)
|
||||
vis.Walk(x.Domain)
|
||||
vis.Walk(x.Body)
|
||||
case *SomeDecl:
|
||||
for i := range x.Symbols {
|
||||
vis.Walk(x.Symbols[i])
|
||||
}
|
||||
}
|
||||
return v1.NewVarVisitor()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user