diff --git a/v1/ast/interning.go b/v1/ast/interning.go index 65ffd0b591..faf5f784a0 100644 --- a/v1/ast/interning.go +++ b/v1/ast/interning.go @@ -57,7 +57,12 @@ var ( "internal": Var("internal"), "else": Var("else"), - "i": Var("i"), "j": Var("j"), "k": Var("k"), "v": Var("v"), "x": Var("x"), "y": Var("y"), "z": Var("z"), + "a": Var("a"), "b": Var("b"), "c": Var("c"), + "i": Var("i"), "j": Var("j"), + "k": Var("k"), "v": Var("v"), + "x": Var("x"), "y": Var("y"), "z": Var("z"), + + "allow": Var("allow"), "deny": Var("deny"), } ) diff --git a/v1/ast/parser_ext.go b/v1/ast/parser_ext.go index 8b63182c0f..c58f3c9103 100644 --- a/v1/ast/parser_ext.go +++ b/v1/ast/parser_ext.go @@ -163,7 +163,6 @@ 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, errors.New("multiple expressions cannot be used for rule head") } @@ -174,7 +173,6 @@ func ParseRuleFromBody(module *Module, body Body) (*Rule, error) { // 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, errors.New("expressions using with keyword cannot be used for rule head") } @@ -224,7 +222,6 @@ func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) { } 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. @@ -257,15 +254,11 @@ func parseCompleteRuleFromEq(module *Module, expr *Expr) (rule *Rule, err error) // 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 + if err == nil { + rule.Head.Assign = true } - - rule.Head.Assign = true - - return rule, nil + return rule, err } // ParseCompleteDocRuleFromEqExpr returns a rule if the expression can be @@ -458,7 +451,7 @@ func ParseImports(input string) ([]*Import, error) { if err != nil { return nil, err } - result := []*Import{} + result := make([]*Import, 0, len(stmts)) for _, stmt := range stmts { if imp, ok := stmt.(*Import); ok { result = append(result, imp) @@ -496,12 +489,17 @@ 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 } + if len(stmts) == 1 { + if body, ok := stmts[0].(Body); ok { + return body, nil + } + } + result := Body{} for _, stmt := range stmts { @@ -619,14 +617,7 @@ func ParseRule(input string) (*Rule, error) { // this function expects *exactly* one statement. If multiple // statements are parsed, an error is returned. func ParseStatement(input string) (Statement, error) { - stmts, _, err := ParseStatements("", input) - if err != nil { - return nil, err - } - if len(stmts) != 1 { - return nil, errors.New("expected exactly one statement") - } - return stmts[0], nil + return ParseStatementWithOpts(input, ParserOptions{}) } func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) { @@ -640,17 +631,24 @@ func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error return stmts[0], nil } -// ParseStatements is deprecated. Use ParseStatementWithOpts instead. +// ParseStatements returns a slice of parsed statements. +// +// Deprecated: Use [ParseStatementsWithOpts] instead. func ParseStatements(filename, input string) ([]Statement, []*Comment, error) { return ParseStatementsWithOpts(filename, input, ParserOptions{}) } -// ParseStatementsWithOpts returns a slice of parsed statements. This is the -// default return value from the parser. +// ParseStatementsWithOpts returns a slice of parsed statements. +// This is the default return value from [*Parser.Parse]. func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Statement, []*Comment, error) { + sr := StringReaderPool.Get() + defer StringReaderPool.Put(sr) + + sr.Reset(input) + parser := NewParser(). WithFilename(filename). - WithReader(strings.NewReader(input)). + WithReader(sr). WithProcessAnnotation(popts.ProcessAnnotation). WithFutureKeywords(popts.FutureKeywords...). WithAllFutureKeywords(popts.AllFutureKeywords). diff --git a/v1/ast/syncpools.go b/v1/ast/syncpools.go index 500bb073cf..f540f4a65e 100644 --- a/v1/ast/syncpools.go +++ b/v1/ast/syncpools.go @@ -2,15 +2,17 @@ package ast import ( "bytes" + "strings" "sync" "github.com/open-policy-agent/opa/v1/util" ) var ( - TermPtrPool = util.NewSyncPool[Term]() - BytesReaderPool = util.NewSyncPool[bytes.Reader]() - IndexResultPool = util.NewSyncPool[IndexResult]() + TermPtrPool = util.NewSyncPool[Term]() + BytesReaderPool = util.NewSyncPool[bytes.Reader]() + StringReaderPool = util.NewSyncPool[strings.Reader]() + IndexResultPool = util.NewSyncPool[IndexResult]() // Needs custom pool because of custom Put logic. varVisitorPool = &vvPool{ diff --git a/v1/ast/term.go b/v1/ast/term.go index f28ac2720a..9de9149246 100644 --- a/v1/ast/term.go +++ b/v1/ast/term.go @@ -120,7 +120,12 @@ func ValueFromReader(r io.Reader) (Value, error) { // As converts v into a Go native type referred to by x. func As(v Value, x any) error { - return util.NewJSONDecoder(strings.NewReader(v.String())).Decode(x) + sr := StringReaderPool.Get() + defer StringReaderPool.Put(sr) + + sr.Reset(v.String()) + + return util.NewJSONDecoder(sr).Decode(x) } // Resolver defines the interface for resolving references to native Go values.