Files
kubesphere/vendor/github.com/open-policy-agent/opa/ast/parser_internal.go
hongming 9769357005 update
Signed-off-by: hongming <talonwan@yunify.com>
2020-03-20 02:16:11 +08:00

621 lines
14 KiB
Go

// Copyright 2018 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.op
package ast
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
)
const (
// commentsKey is the global map key for the comments slice.
commentsKey = "comments"
// filenameKey is the global map key for the filename.
filenameKey = "filename"
)
type program struct {
buf []interface{}
comments interface{}
}
type ruleExt struct {
loc *Location
term *Term
body Body
}
// currentLocation converts the parser context to a Location object.
func currentLocation(c *current) *Location {
return NewLocation(c.text, c.globalStore[filenameKey].(string), c.pos.line, c.pos.col)
}
func makeProgram(c *current, vals interface{}) (interface{}, error) {
var buf []interface{}
if vals == nil {
return buf, nil
}
ifaceSlice := vals.([]interface{})
head := ifaceSlice[0]
buf = append(buf, head)
for _, tail := range ifaceSlice[1].([]interface{}) {
stmt := tail.([]interface{})[1]
buf = append(buf, stmt)
}
return program{buf, c.globalStore[commentsKey]}, nil
}
func makePackage(loc *Location, value interface{}) (interface{}, error) {
// All packages are implicitly declared under the default root document.
term := value.(*Term)
path := Ref{DefaultRootDocument.Copy().SetLocation(term.Location)}
switch v := term.Value.(type) {
case Ref:
// Convert head of package Ref to String because it will be prefixed
// with the root document variable.
head := StringTerm(string(v[0].Value.(Var))).SetLocation(v[0].Location)
tail := v[1:]
if !tail.IsGround() {
return nil, fmt.Errorf("package name cannot contain variables: %v", v)
}
// We do not allow non-string values in package names.
// Because documents are typically represented as JSON, non-string keys are
// not allowed for now.
// TODO(tsandall): consider special syntax for namespacing under arrays.
for _, p := range tail {
_, ok := p.Value.(String)
if !ok {
return nil, fmt.Errorf("package name cannot contain non-string values: %v", v)
}
}
path = append(path, head)
path = append(path, tail...)
case Var:
s := StringTerm(string(v)).SetLocation(term.Location)
path = append(path, s)
}
pkg := &Package{Location: loc, Path: path}
return pkg, nil
}
func makeImport(loc *Location, path, alias interface{}) (interface{}, error) {
imp := &Import{}
imp.Location = loc
imp.Path = path.(*Term)
if err := IsValidImportPath(imp.Path.Value); err != nil {
return nil, err
}
if alias == nil {
return imp, nil
}
aliasSlice := alias.([]interface{})
// Import definition above describes the "alias" slice. We only care about the "Var" element.
imp.Alias = aliasSlice[3].(*Term).Value.(Var)
return imp, nil
}
func makeDefaultRule(loc *Location, name, operator, value interface{}) (interface{}, error) {
if string(operator.([]uint8)) == Assign.Infix {
return nil, fmt.Errorf("default rules must use = operator (not := operator)")
}
term := value.(*Term)
var err error
vis := NewGenericVisitor(func(x interface{}) bool {
if err != nil {
return true
}
switch x.(type) {
case *ArrayComprehension, *ObjectComprehension, *SetComprehension: // skip closures
return true
case Ref, Var:
err = fmt.Errorf("default rule value cannot contain %v", TypeName(x))
return true
}
return false
})
vis.Walk(term)
if err != nil {
return nil, err
}
body := NewBody(NewExpr(BooleanTerm(true).SetLocation(loc)))
rule := &Rule{
Location: loc,
Default: true,
Head: &Head{
Location: loc,
Name: name.(*Term).Value.(Var),
Value: value.(*Term),
},
Body: body,
}
rule.Body[0].Location = loc
return []*Rule{rule}, nil
}
func makeRule(loc *Location, head, rest interface{}) (interface{}, error) {
if head == nil {
return nil, nil
}
sl := rest.([]interface{})
rules := []*Rule{
{
Location: loc,
Head: head.(*Head),
Body: sl[0].(Body),
},
}
var ordered bool
prev := rules[0]
for i, elem := range sl[1].([]interface{}) {
next := elem.([]interface{})
re := next[1].(ruleExt)
if rules[0].Head.Assign {
return nil, errElseAssignOperator
}
if re.term == nil {
if ordered {
return nil, fmt.Errorf("expected 'else' keyword")
}
rules = append(rules, &Rule{
Location: re.loc,
Head: prev.Head.Copy(),
Body: re.body,
})
} else {
if (rules[0].Head.DocKind() != CompleteDoc) || (i != 0 && !ordered) {
return nil, fmt.Errorf("unexpected 'else' keyword")
}
ordered = true
curr := &Rule{
Location: re.loc,
Head: &Head{
Name: prev.Head.Name,
Args: prev.Head.Args.Copy(),
Value: re.term,
Location: re.term.Location,
},
Body: re.body,
}
prev.Else = curr
prev = curr
}
}
return rules, nil
}
func makeRuleHead(loc *Location, name, args, key, value interface{}) (interface{}, error) {
head := &Head{}
head.Location = loc
head.Name = name.(*Term).Value.(Var)
if args != nil && key != nil {
return nil, fmt.Errorf("partial rules cannot take arguments")
}
if args != nil {
argSlice := args.([]interface{})
head.Args = argSlice[3].(Args)
}
if key != nil {
keySlice := key.([]interface{})
// Head definition above describes the "key" slice. We care about the "Term" element.
head.Key = keySlice[3].(*Term)
}
if value != nil {
valueSlice := value.([]interface{})
operator := string(valueSlice[1].([]uint8))
if operator == Assign.Infix {
if head.Key != nil {
return nil, errPartialRuleAssignOperator
} else if len(head.Args) > 0 {
return nil, errFunctionAssignOperator
}
head.Assign = true
}
// Head definition above describes the "value" slice. We care about the "Term" element.
head.Value = valueSlice[len(valueSlice)-1].(*Term)
}
if key == nil && value == nil {
head.Value = BooleanTerm(true).SetLocation(head.Location)
}
if key != nil && value != nil {
switch head.Key.Value.(type) {
case Var, String, Ref: // nop
default:
return nil, fmt.Errorf("object key must be string, var, or ref, not %v", TypeName(head.Key.Value))
}
}
return head, nil
}
func makeArgs(list interface{}) (interface{}, error) {
termSlice := list.([]*Term)
args := make(Args, len(termSlice))
for i := 0; i < len(args); i++ {
args[i] = termSlice[i]
}
return args, nil
}
func makeRuleExt(loc *Location, val, b interface{}) (interface{}, error) {
bs := b.([]interface{})
body := bs[1].(Body)
if val == nil {
term := BooleanTerm(true)
term.Location = loc
return ruleExt{term.Location, term, body}, nil
}
vs := val.([]interface{})
t := vs[3].(*Term)
return ruleExt{loc, t, body}, nil
}
func makeLiteral(negated, value, with interface{}) (interface{}, error) {
expr := value.(*Expr)
expr.Negated = negated.(bool)
if with != nil {
expr.With = with.([]*With)
}
return expr, nil
}
func makeLiteralExpr(loc *Location, lhs, rest interface{}) (interface{}, error) {
if rest == nil {
if call, ok := lhs.(*Term).Value.(Call); ok {
return NewExpr([]*Term(call)).SetLocation(loc), nil
}
return NewExpr(lhs).SetLocation(loc), nil
}
termSlice := rest.([]interface{})
terms := []*Term{
termSlice[1].(*Term),
lhs.(*Term),
termSlice[3].(*Term),
}
expr := NewExpr(terms).SetLocation(loc)
return expr, nil
}
func makeSomeDeclLiteral(loc *Location, sl interface{}) (interface{}, error) {
symbols := sl.([]*Term)
return NewExpr(&SomeDecl{Location: loc, Symbols: symbols}).SetLocation(loc), nil
}
func makeSomeDeclSymbols(head interface{}, rest interface{}) (interface{}, error) {
var symbols []*Term
symbols = append(symbols, head.(*Term))
if sl1, ok := rest.([]interface{}); ok {
for i := range sl1 {
if sl2, ok := sl1[i].([]interface{}); ok {
symbols = append(symbols, sl2[3].(*Term))
}
}
}
return symbols, nil
}
func makeWithKeywordList(head, tail interface{}) (interface{}, error) {
var withs []*With
if head == nil {
return withs, nil
}
sl := tail.([]interface{})
withs = make([]*With, 0, len(sl)+1)
withs = append(withs, head.(*With))
for i := range sl {
withSlice := sl[i].([]interface{})
withs = append(withs, withSlice[1].(*With))
}
return withs, nil
}
func makeWithKeyword(loc *Location, target, value interface{}) (interface{}, error) {
w := &With{
Target: target.(*Term),
Value: value.(*Term),
}
return w.SetLocation(loc), nil
}
func makeExprTerm(loc *Location, lhs, rest interface{}) (interface{}, error) {
if rest == nil {
return lhs, nil
}
sl := rest.([]interface{})
if len(sl) == 0 {
return lhs, nil
}
for i := range sl {
termSlice := sl[i].([]interface{})
call := Call{
termSlice[1].(*Term),
lhs.(*Term),
termSlice[3].(*Term),
}
lhs = NewTerm(call).SetLocation(loc)
}
return lhs, nil
}
func makeCall(loc *Location, operator, args interface{}) (interface{}, error) {
termSlice := args.([]*Term)
termOperator := operator.(*Term)
call := make(Call, len(termSlice)+1)
if _, ok := termOperator.Value.(Var); ok {
termOperator = RefTerm(termOperator).SetLocation(loc)
}
call[0] = termOperator
for i := 1; i < len(call); i++ {
call[i] = termSlice[i-1]
}
return NewTerm(call).SetLocation(loc), nil
}
func makeBraceEnclosedBody(loc *Location, body interface{}) (interface{}, error) {
if body != nil {
return body, nil
}
return NewBody(NewExpr(ObjectTerm().SetLocation(loc)).SetLocation(loc)), nil
}
func makeBody(head, tail interface{}, pos int) (interface{}, error) {
sl := tail.([]interface{})
body := make(Body, len(sl)+1)
body[0] = head.(*Expr)
for i := 1; i < len(body); i++ {
body[i] = sl[i-1].([]interface{})[pos].(*Expr)
}
return body, nil
}
func makeExprTermList(head, tail interface{}) (interface{}, error) {
var terms []*Term
if head == nil {
return terms, nil
}
sl := tail.([]interface{})
terms = make([]*Term, 0, len(sl)+1)
terms = append(terms, head.(*Term))
for i := range sl {
termSlice := sl[i].([]interface{})
terms = append(terms, termSlice[3].(*Term))
}
return terms, nil
}
func makeExprTermPairList(head, tail interface{}) (interface{}, error) {
var terms [][2]*Term
if head == nil {
return terms, nil
}
sl := tail.([]interface{})
terms = make([][2]*Term, 0, len(sl)+1)
terms = append(terms, head.([2]*Term))
for i := range sl {
termSlice := sl[i].([]interface{})
terms = append(terms, termSlice[3].([2]*Term))
}
return terms, nil
}
func makeExprTermPair(key, value interface{}) (interface{}, error) {
return [2]*Term{key.(*Term), value.(*Term)}, nil
}
func makeInfixOperator(loc *Location, text []byte) (interface{}, error) {
op := string(text)
for _, b := range Builtins {
if string(b.Infix) == op {
op = string(b.Name)
}
}
operator := RefTerm(VarTerm(op).SetLocation(loc)).SetLocation(loc)
return operator, nil
}
func makeArray(loc *Location, list interface{}) (interface{}, error) {
termSlice := list.([]*Term)
return ArrayTerm(termSlice...).SetLocation(loc), nil
}
func makeObject(loc *Location, list interface{}) (interface{}, error) {
termPairSlice := list.([][2]*Term)
return ObjectTerm(termPairSlice...).SetLocation(loc), nil
}
func makeSet(loc *Location, list interface{}) (interface{}, error) {
termSlice := list.([]*Term)
return SetTerm(termSlice...).SetLocation(loc), nil
}
func makeArrayComprehension(loc *Location, head, body interface{}) (interface{}, error) {
return ArrayComprehensionTerm(head.(*Term), body.(Body)).SetLocation(loc), nil
}
func makeSetComprehension(loc *Location, head, body interface{}) (interface{}, error) {
return SetComprehensionTerm(head.(*Term), body.(Body)).SetLocation(loc), nil
}
func makeObjectComprehension(loc *Location, head, body interface{}) (interface{}, error) {
pair := head.([2]*Term)
return ObjectComprehensionTerm(pair[0], pair[1], body.(Body)).SetLocation(loc), nil
}
func makeRef(loc *Location, head, rest interface{}) (interface{}, error) {
headTerm := head.(*Term)
ifaceSlice := rest.([]interface{})
if len(ifaceSlice) == 0 {
return headTerm, nil
}
ref := make(Ref, len(ifaceSlice)+1)
ref[0] = headTerm
for i := 1; i < len(ref); i++ {
ref[i] = ifaceSlice[i-1].(*Term)
}
return NewTerm(ref).SetLocation(loc), nil
}
func makeRefOperandDot(loc *Location, val interface{}) (interface{}, error) {
return StringTerm(string(val.(*Term).Value.(Var))).SetLocation(loc), nil
}
func makeVar(loc *Location, text interface{}) (interface{}, error) {
str := string(text.([]byte))
return VarTerm(str).SetLocation(loc), nil
}
func makeNumber(loc *Location, text interface{}) (interface{}, error) {
f, ok := new(big.Float).SetString(string(text.([]byte)))
if !ok {
// This indicates the grammar is out-of-sync with what the string
// representation of floating point numbers. This should not be
// possible.
panic("illegal value")
}
// Put limit on size of exponent to prevent non-linear cost of String()
// function on big.Float from causing denial of service: https://github.com/golang/go/issues/11068
//
// n == sign * mantissa * 2^exp
// 0.5 <= mantissa < 1.0
//
// The limit is arbitrary.
exp := f.MantExp(nil)
if exp > 1e5 || exp < -1e5 {
return nil, fmt.Errorf("number too big")
}
return NumberTerm(json.Number(f.String())).SetLocation(loc), nil
}
func makeString(loc *Location, text interface{}) (interface{}, error) {
var v string
err := json.Unmarshal(text.([]byte), &v)
return StringTerm(v).SetLocation(loc), err
}
func makeRawString(loc *Location, text interface{}) (interface{}, error) {
s := string(text.([]byte))
s = s[1 : len(s)-1] // Trim surrounding quotes.
return StringTerm(s).SetLocation(loc), nil
}
func makeNonterminatedString(loc *Location, s string) (interface{}, error) {
return StringTerm(s).SetLocation(loc), fmt.Errorf("found non-terminated string literal")
}
func makeBool(loc *Location, text interface{}) (interface{}, error) {
var term *Term
if string(text.([]byte)) == "true" {
term = BooleanTerm(true)
} else {
term = BooleanTerm(false)
}
return term.SetLocation(loc), nil
}
func makeNull(loc *Location) (interface{}, error) {
return NullTerm().SetLocation(loc), nil
}
func makeComments(c *current, text interface{}) (interface{}, error) {
var buf bytes.Buffer
for _, x := range text.([]interface{}) {
buf.Write(x.([]byte))
}
comment := NewComment(buf.Bytes())
comment.Location = currentLocation(c)
comments := c.globalStore[commentsKey].(map[commentKey]*Comment)
key := commentKey{
File: comment.Location.File,
Row: comment.Location.Row,
Col: comment.Location.Col,
}
comments[key] = comment
return comment, nil
}