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:
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user