influxdb/query/interpreter/interpreter.go

1109 lines
29 KiB
Go

package interpreter
import (
"fmt"
"regexp"
"github.com/influxdata/platform/query/ast"
"github.com/influxdata/platform/query/semantic"
"github.com/influxdata/platform/query/values"
"github.com/pkg/errors"
)
// Interpreter used to interpret a Flux program
type Interpreter struct {
values []values.Value
options *Scope
globals *Scope
}
// NewInterpreter instantiates a new Flux Interpreter
func NewInterpreter(options, builtins map[string]values.Value) *Interpreter {
optionScope := NewScopeWithValues(options)
globalScope := optionScope.NestWithValues(builtins)
interpreter := new(Interpreter)
interpreter.options = optionScope
interpreter.globals = globalScope
return interpreter
}
// Return gives the return value from the block
func (itrp *Interpreter) Return() values.Value {
return itrp.globals.Return()
}
// GlobalScope returns a pointer to the global scope of the program.
// That is the scope nested directly below the options scope.
func (itrp *Interpreter) GlobalScope() *Scope {
return itrp.globals
}
// SetVar adds a variable binding to the global scope
func (itrp *Interpreter) SetVar(name string, val values.Value) {
itrp.globals.Set(name, val)
}
// SideEffects returns the evaluated expressions of a Flux program
func (itrp *Interpreter) SideEffects() []values.Value {
return itrp.values
}
// Option returns a Flux option by name
func (itrp *Interpreter) Option(name string) values.Value {
return itrp.options.Get(name)
}
// SetOption sets a new option binding
func (itrp *Interpreter) SetOption(name string, val values.Value) {
itrp.options.Set(name, val)
}
// Eval evaluates the expressions composing a Flux program.
func (itrp *Interpreter) Eval(program *semantic.Program) error {
return itrp.eval(program)
}
func (itrp *Interpreter) eval(program *semantic.Program) error {
topLevelScope := itrp.globals
for _, stmt := range program.Body {
val, err := itrp.doStatement(stmt, topLevelScope)
if err != nil {
return err
}
if val != nil {
itrp.values = append(itrp.values, val)
}
}
return nil
}
// doStatement returns the resolved value of a top-level statement
func (itrp *Interpreter) doStatement(stmt semantic.Statement, scope *Scope) (values.Value, error) {
scope.SetReturn(values.InvalidValue)
switch s := stmt.(type) {
case *semantic.OptionStatement:
return itrp.doOptionStatement(s.Declaration.(*semantic.NativeVariableDeclaration), scope)
case *semantic.NativeVariableDeclaration:
return itrp.doVariableDeclaration(s, scope)
case *semantic.ExpressionStatement:
v, err := itrp.doExpression(s.Expression, scope)
if err != nil {
return nil, err
}
scope.SetReturn(v)
return v, nil
case *semantic.BlockStatement:
nested := scope.Nest()
for i, stmt := range s.Body {
_, err := itrp.doStatement(stmt, nested)
if err != nil {
return nil, err
}
// Validate a return statement is the last statement
if _, ok := stmt.(*semantic.ReturnStatement); ok {
if i != len(s.Body)-1 {
return nil, errors.New("return statement is not the last statement in the block")
}
}
}
// Propgate any return value from the nested scope out. Since a return statement is
// always last we do not have to worry about overriding an existing return value.
scope.SetReturn(nested.Return())
case *semantic.ReturnStatement:
v, err := itrp.doExpression(s.Argument, scope)
if err != nil {
return nil, err
}
scope.SetReturn(v)
default:
return nil, fmt.Errorf("unsupported statement type %T", stmt)
}
return nil, nil
}
func (itrp *Interpreter) doOptionStatement(declaration *semantic.NativeVariableDeclaration, scope *Scope) (values.Value, error) {
value, err := itrp.doExpression(declaration.Init, scope)
if err != nil {
return nil, err
}
itrp.options.Set(declaration.Identifier.Name, value)
return value, nil
}
func (itrp *Interpreter) doVariableDeclaration(declaration *semantic.NativeVariableDeclaration, scope *Scope) (values.Value, error) {
value, err := itrp.doExpression(declaration.Init, scope)
if err != nil {
return nil, err
}
scope.Set(declaration.Identifier.Name, value)
return value, nil
}
func (itrp *Interpreter) doExpression(expr semantic.Expression, scope *Scope) (values.Value, error) {
switch e := expr.(type) {
case semantic.Literal:
return itrp.doLiteral(e)
case *semantic.ArrayExpression:
return itrp.doArray(e, scope)
case *semantic.IdentifierExpression:
value, ok := scope.Lookup(e.Name)
if !ok {
return nil, fmt.Errorf("undefined identifier %q", e.Name)
}
return value, nil
case *semantic.CallExpression:
v, err := itrp.doCall(e, scope)
if err != nil {
// Determine function name
return nil, errors.Wrapf(err, "error calling function %q", functionName(e))
}
return v, nil
case *semantic.MemberExpression:
obj, err := itrp.doExpression(e.Object, scope)
if err != nil {
return nil, err
}
v, ok := obj.Object().Get(e.Property)
if !ok {
return nil, fmt.Errorf("object has no property %q", e.Property)
}
return v, nil
case *semantic.ObjectExpression:
return itrp.doObject(e, scope)
case *semantic.UnaryExpression:
v, err := itrp.doExpression(e.Argument, scope)
if err != nil {
return nil, err
}
switch e.Operator {
case ast.NotOperator:
if v.Type() != semantic.Bool {
return nil, fmt.Errorf("operand to unary expression is not a boolean value, got %v", v.Type())
}
return values.NewBoolValue(!v.Bool()), nil
case ast.SubtractionOperator:
switch t := v.Type(); t {
case semantic.Int:
return values.NewIntValue(-v.Int()), nil
case semantic.Float:
return values.NewFloatValue(-v.Float()), nil
case semantic.Duration:
return values.NewDurationValue(-v.Duration()), nil
default:
return nil, fmt.Errorf("operand to unary expression is not a number value, got %v", v.Type())
}
default:
return nil, fmt.Errorf("unsupported operator %q to unary expression", e.Operator)
}
case *semantic.BinaryExpression:
l, err := itrp.doExpression(e.Left, scope)
if err != nil {
return nil, err
}
r, err := itrp.doExpression(e.Right, scope)
if err != nil {
return nil, err
}
bf, err := values.LookupBinaryFunction(values.BinaryFuncSignature{
Operator: e.Operator,
Left: l.Type(),
Right: r.Type(),
})
if err != nil {
return nil, err
}
return bf(l, r), nil
case *semantic.LogicalExpression:
l, err := itrp.doExpression(e.Left, scope)
if err != nil {
return nil, err
}
if l.Type() != semantic.Bool {
return nil, fmt.Errorf("left operand to logcial expression is not a boolean value, got %v", l.Type())
}
left := l.Bool()
if e.Operator == ast.AndOperator && !left {
// Early return
return values.NewBoolValue(false), nil
} else if e.Operator == ast.OrOperator && left {
// Early return
return values.NewBoolValue(true), nil
}
r, err := itrp.doExpression(e.Right, scope)
if err != nil {
return nil, err
}
if r.Type() != semantic.Bool {
return nil, errors.New("right operand to logcial expression is not a boolean value")
}
right := r.Bool()
switch e.Operator {
case ast.AndOperator:
return values.NewBoolValue(left && right), nil
case ast.OrOperator:
return values.NewBoolValue(left || right), nil
default:
return nil, fmt.Errorf("invalid logical operator %v", e.Operator)
}
case *semantic.FunctionExpression:
return &function{
e: e,
scope: scope.Nest(),
}, nil
default:
return nil, fmt.Errorf("unsupported expression %T", expr)
}
}
func (itrp *Interpreter) doArray(a *semantic.ArrayExpression, scope *Scope) (values.Value, error) {
elements := make([]values.Value, len(a.Elements))
elementType := semantic.EmptyArrayType.ElementType()
for i, el := range a.Elements {
v, err := itrp.doExpression(el, scope)
if err != nil {
return nil, err
}
if i == 0 {
elementType = v.Type()
}
if elementType != v.Type() {
return nil, fmt.Errorf("cannot mix types in an array, found both %v and %v", elementType, v.Type())
}
elements[i] = v
}
return values.NewArrayWithBacking(elementType, elements), nil
}
func (itrp *Interpreter) doObject(m *semantic.ObjectExpression, scope *Scope) (values.Value, error) {
obj := values.NewObject()
for _, p := range m.Properties {
v, err := itrp.doExpression(p.Value, scope)
if err != nil {
return nil, err
}
if _, ok := obj.Get(p.Key.Name); ok {
return nil, fmt.Errorf("duplicate key in object: %q", p.Key.Name)
}
obj.Set(p.Key.Name, v)
}
return obj, nil
}
func (itrp *Interpreter) doLiteral(lit semantic.Literal) (values.Value, error) {
switch l := lit.(type) {
case *semantic.DateTimeLiteral:
return values.NewTimeValue(values.Time(l.Value.UnixNano())), nil
case *semantic.DurationLiteral:
return values.NewDurationValue(values.Duration(l.Value)), nil
case *semantic.FloatLiteral:
return values.NewFloatValue(l.Value), nil
case *semantic.IntegerLiteral:
return values.NewIntValue(l.Value), nil
case *semantic.UnsignedIntegerLiteral:
return values.NewUIntValue(l.Value), nil
case *semantic.StringLiteral:
return values.NewStringValue(l.Value), nil
case *semantic.RegexpLiteral:
return values.NewRegexpValue(l.Value), nil
case *semantic.BooleanLiteral:
return values.NewBoolValue(l.Value), nil
default:
return nil, fmt.Errorf("unknown literal type %T", lit)
}
}
func functionName(call *semantic.CallExpression) string {
switch callee := call.Callee.(type) {
case *semantic.IdentifierExpression:
return callee.Name
case *semantic.MemberExpression:
return callee.Property
default:
return "<anonymous function>"
}
}
func DoFunctionCall(f func(args Arguments) (values.Value, error), argsObj values.Object) (values.Value, error) {
args := NewArguments(argsObj)
v, err := f(args)
if err != nil {
return nil, err
}
if unused := args.listUnused(); len(unused) > 0 {
return nil, fmt.Errorf("unused arguments %v", unused)
}
return v, nil
}
func (itrp *Interpreter) doCall(call *semantic.CallExpression, scope *Scope) (values.Value, error) {
callee, err := itrp.doExpression(call.Callee, scope)
if err != nil {
return nil, err
}
if callee.Type().Kind() != semantic.Function {
return nil, fmt.Errorf("cannot call function, value is of type %v", callee.Type())
}
f := callee.Function()
argObj, err := itrp.doArguments(call.Arguments, scope)
if err != nil {
return nil, err
}
// Check if the function is an interpFunction and rebind it.
if af, ok := f.(*function); ok {
af.itrp = itrp
f = af
}
// Call the function
value, err := f.Call(argObj)
if err != nil {
return nil, err
}
if f.HasSideEffect() {
itrp.values = append(itrp.values, value)
}
return value, nil
}
func (itrp *Interpreter) doArguments(args *semantic.ObjectExpression, scope *Scope) (values.Object, error) {
obj := values.NewObject()
if args == nil || len(args.Properties) == 0 {
return obj, nil
}
for _, p := range args.Properties {
value, err := itrp.doExpression(p.Value, scope)
if err != nil {
return nil, err
}
if _, ok := obj.Get(p.Key.Name); ok {
return nil, fmt.Errorf("duplicate keyword parameter specified: %q", p.Key.Name)
}
obj.Set(p.Key.Name, value)
}
return obj, nil
}
// TODO(Josh): Scope methods should be private
type Scope struct {
parent *Scope
values map[string]values.Value
returnValue values.Value
}
func NewScope() *Scope {
return &Scope{
values: make(map[string]values.Value),
}
}
func NewScopeWithValues(vals map[string]values.Value) *Scope {
cp := make(map[string]values.Value, len(vals))
for k, v := range vals {
cp[k] = v
}
return &Scope{
values: cp,
}
}
func (s *Scope) Get(name string) values.Value {
return s.values[name]
}
func (s *Scope) Set(name string, value values.Value) {
s.values[name] = value
}
func (s *Scope) Values() map[string]values.Value {
cp := make(map[string]values.Value, len(s.values))
for k, v := range s.values {
cp[k] = v
}
return cp
}
func (s *Scope) SetValues(vals map[string]values.Value) {
for k, v := range vals {
s.values[k] = v
}
}
func (s *Scope) Lookup(name string) (values.Value, bool) {
if s == nil {
return nil, false
}
v, ok := s.values[name]
if !ok {
return s.parent.Lookup(name)
}
return v, ok
}
// SetReturn sets the return value of this scope.
func (s *Scope) SetReturn(value values.Value) {
s.returnValue = value
}
// Return reports the return value for this scope. If no return value has been set a value with type semantic.TInvalid is returned.
func (s *Scope) Return() values.Value {
return s.returnValue
}
func (s *Scope) Names() []string {
if s == nil {
return nil
}
names := s.parent.Names()
for k := range s.values {
names = append(names, k)
}
return names
}
// Nest returns a new nested scope.
func (s *Scope) Nest() *Scope {
c := NewScope()
c.parent = s
return c
}
func (s *Scope) NestWithValues(values map[string]values.Value) *Scope {
c := NewScopeWithValues(values)
c.parent = s
return c
}
// Copy returns a copy of the scope and its parents.
func (s *Scope) Copy() *Scope {
c := NewScope()
// copy parent values into new scope
curr := s
for curr != nil {
// copy values
for k, v := range curr.values {
c.values[k] = v
}
curr = curr.parent
}
return c
}
func (s *Scope) Range(f func(k string, v values.Value)) {
for k, v := range s.values {
f(k, v)
}
if s.parent != nil {
s.parent.Range(f)
}
}
// Value represents any value that can be the result of evaluating any expression.
type Value interface {
// Type reports the type of value
Type() semantic.Type
// Value returns the actual value represented.
Value() interface{}
// Property returns a new value which is a property of this value.
Property(name string) (values.Value, error)
}
type value struct {
t semantic.Type
v interface{}
}
func (v value) Type() semantic.Type {
return v.t
}
func (v value) Value() interface{} {
return v.v
}
func (v value) Property(name string) (values.Value, error) {
return nil, fmt.Errorf("property %q does not exist", name)
}
func (v value) String() string {
return fmt.Sprintf("%v", v.v)
}
type function struct {
e *semantic.FunctionExpression
scope *Scope
call func(Arguments) (values.Value, error)
itrp *Interpreter
}
func (f *function) Type() semantic.Type {
return f.e.Type()
}
func (f *function) Str() string {
panic(values.UnexpectedKind(semantic.Object, semantic.String))
}
func (f *function) Int() int64 {
panic(values.UnexpectedKind(semantic.Object, semantic.Int))
}
func (f *function) UInt() uint64 {
panic(values.UnexpectedKind(semantic.Object, semantic.UInt))
}
func (f *function) Float() float64 {
panic(values.UnexpectedKind(semantic.Object, semantic.Float))
}
func (f *function) Bool() bool {
panic(values.UnexpectedKind(semantic.Object, semantic.Bool))
}
func (f *function) Time() values.Time {
panic(values.UnexpectedKind(semantic.Object, semantic.Time))
}
func (f *function) Duration() values.Duration {
panic(values.UnexpectedKind(semantic.Object, semantic.Duration))
}
func (f *function) Regexp() *regexp.Regexp {
panic(values.UnexpectedKind(semantic.Object, semantic.Regexp))
}
func (f *function) Array() values.Array {
panic(values.UnexpectedKind(semantic.Object, semantic.Function))
}
func (f *function) Object() values.Object {
panic(values.UnexpectedKind(semantic.Object, semantic.Object))
}
func (f *function) Function() values.Function {
return f
}
func (f *function) Equal(rhs values.Value) bool {
if f.Type() != rhs.Type() {
return false
}
v, ok := rhs.(*function)
return ok && (f == v)
}
func (f *function) HasSideEffect() bool {
// Function definitions do not produce side effects.
// Only a function call expression can produce side effects.
return false
}
func (f *function) Call(argsObj values.Object) (values.Value, error) {
args := newArguments(argsObj)
v, err := f.doCall(args)
if err != nil {
return nil, err
}
if unused := args.listUnused(); len(unused) > 0 {
return nil, fmt.Errorf("unused arguments %s", unused)
}
return v, nil
}
func (f *function) doCall(args Arguments) (values.Value, error) {
for _, p := range f.e.Params {
if p.Default == nil {
v, err := args.GetRequired(p.Key.Name)
if err != nil {
return nil, err
}
f.scope.Set(p.Key.Name, v)
} else {
v, ok := args.Get(p.Key.Name)
if !ok {
// Use default value
var err error
v, err = f.itrp.doExpression(p.Default, f.scope)
if err != nil {
return nil, err
}
}
f.scope.Set(p.Key.Name, v)
}
}
switch n := f.e.Body.(type) {
case semantic.Expression:
return f.itrp.doExpression(n, f.scope)
case semantic.Statement:
_, err := f.itrp.doStatement(n, f.scope)
if err != nil {
return nil, err
}
v := f.scope.Return()
if v.Type() == semantic.Invalid {
return nil, errors.New("function has no return value")
}
return v, nil
default:
return nil, fmt.Errorf("unsupported function body type %T", f.e.Body)
}
}
// Resolver represents a value that can resolve itself
type Resolver interface {
Resolve() (semantic.Node, error)
}
func ResolveFunction(f values.Function) (*semantic.FunctionExpression, error) {
resolver, ok := f.(Resolver)
if !ok {
return nil, errors.New("function is not resolvable")
}
resolved, err := resolver.Resolve()
if err != nil {
return nil, err
}
fn, ok := resolved.(*semantic.FunctionExpression)
if !ok {
return nil, errors.New("resolved function is not a function")
}
return fn, nil
}
// Resolve rewrites the function resolving any identifiers not listed in the function params.
func (f *function) Resolve() (semantic.Node, error) {
n := f.e.Copy()
node, err := f.resolveIdentifiers(n)
if err != nil {
return nil, err
}
return node, nil
}
func (f function) resolveIdentifiers(n semantic.Node) (semantic.Node, error) {
switch n := n.(type) {
case *semantic.IdentifierExpression:
for _, p := range f.e.Params {
if n.Name == p.Key.Name {
// Identifier is a parameter do not resolve
return n, nil
}
}
v, ok := f.scope.Lookup(n.Name)
if !ok {
return nil, fmt.Errorf("name %q does not exist in scope", n.Name)
}
return resolveValue(v)
case *semantic.BlockStatement:
for i, s := range n.Body {
node, err := f.resolveIdentifiers(s)
if err != nil {
return nil, err
}
n.Body[i] = node.(semantic.Statement)
}
case *semantic.OptionStatement:
node, err := f.resolveIdentifiers(n.Declaration)
if err != nil {
return nil, err
}
n.Declaration = node.(semantic.VariableDeclaration)
case *semantic.ExpressionStatement:
node, err := f.resolveIdentifiers(n.Expression)
if err != nil {
return nil, err
}
n.Expression = node.(semantic.Expression)
case *semantic.ReturnStatement:
node, err := f.resolveIdentifiers(n.Argument)
if err != nil {
return nil, err
}
n.Argument = node.(semantic.Expression)
case *semantic.NativeVariableDeclaration:
node, err := f.resolveIdentifiers(n.Init)
if err != nil {
return nil, err
}
n.Init = node.(semantic.Expression)
case *semantic.CallExpression:
node, err := f.resolveIdentifiers(n.Arguments)
if err != nil {
return nil, err
}
n.Arguments = node.(*semantic.ObjectExpression)
case *semantic.FunctionExpression:
node, err := f.resolveIdentifiers(n.Body)
if err != nil {
return nil, err
}
n.Body = node
case *semantic.BinaryExpression:
node, err := f.resolveIdentifiers(n.Left)
if err != nil {
return nil, err
}
n.Left = node.(semantic.Expression)
node, err = f.resolveIdentifiers(n.Right)
if err != nil {
return nil, err
}
n.Right = node.(semantic.Expression)
case *semantic.UnaryExpression:
node, err := f.resolveIdentifiers(n.Argument)
if err != nil {
return nil, err
}
n.Argument = node.(semantic.Expression)
case *semantic.LogicalExpression:
node, err := f.resolveIdentifiers(n.Left)
if err != nil {
return nil, err
}
n.Left = node.(semantic.Expression)
node, err = f.resolveIdentifiers(n.Right)
if err != nil {
return nil, err
}
n.Right = node.(semantic.Expression)
case *semantic.ArrayExpression:
for i, el := range n.Elements {
node, err := f.resolveIdentifiers(el)
if err != nil {
return nil, err
}
n.Elements[i] = node.(semantic.Expression)
}
case *semantic.ObjectExpression:
for i, p := range n.Properties {
node, err := f.resolveIdentifiers(p)
if err != nil {
return nil, err
}
n.Properties[i] = node.(*semantic.Property)
}
case *semantic.ConditionalExpression:
node, err := f.resolveIdentifiers(n.Test)
if err != nil {
return nil, err
}
n.Test = node.(semantic.Expression)
node, err = f.resolveIdentifiers(n.Alternate)
if err != nil {
return nil, err
}
n.Alternate = node.(semantic.Expression)
node, err = f.resolveIdentifiers(n.Consequent)
if err != nil {
return nil, err
}
n.Consequent = node.(semantic.Expression)
case *semantic.Property:
node, err := f.resolveIdentifiers(n.Value)
if err != nil {
return nil, err
}
n.Value = node.(semantic.Expression)
}
return n, nil
}
func resolveValue(v values.Value) (semantic.Node, error) {
switch k := v.Type().Kind(); k {
case semantic.String:
return &semantic.StringLiteral{
Value: v.Str(),
}, nil
case semantic.Int:
return &semantic.IntegerLiteral{
Value: v.Int(),
}, nil
case semantic.UInt:
return &semantic.UnsignedIntegerLiteral{
Value: v.UInt(),
}, nil
case semantic.Float:
return &semantic.FloatLiteral{
Value: v.Float(),
}, nil
case semantic.Bool:
return &semantic.BooleanLiteral{
Value: v.Bool(),
}, nil
case semantic.Time:
return &semantic.DateTimeLiteral{
Value: v.Time().Time(),
}, nil
case semantic.Regexp:
return &semantic.RegexpLiteral{
Value: v.Regexp(),
}, nil
case semantic.Duration:
return &semantic.DurationLiteral{
Value: v.Duration().Duration(),
}, nil
case semantic.Function:
resolver, ok := v.Function().(Resolver)
if !ok {
return nil, fmt.Errorf("function is not resolvable %T", v.Function())
}
return resolver.Resolve()
case semantic.Array:
arr := v.Array()
node := new(semantic.ArrayExpression)
node.Elements = make([]semantic.Expression, arr.Len())
var err error
arr.Range(func(i int, el values.Value) {
if err != nil {
return
}
var n semantic.Node
n, err = resolveValue(el)
if err != nil {
return
}
node.Elements[i] = n.(semantic.Expression)
})
if err != nil {
return nil, err
}
return node, nil
case semantic.Object:
obj := v.Object()
node := new(semantic.ObjectExpression)
node.Properties = make([]*semantic.Property, 0, obj.Len())
var err error
obj.Range(func(k string, v values.Value) {
if err != nil {
return
}
var n semantic.Node
n, err = resolveValue(v)
if err != nil {
return
}
node.Properties = append(node.Properties, &semantic.Property{
Key: &semantic.Identifier{Name: k},
Value: n.(semantic.Expression),
})
})
if err != nil {
return nil, err
}
return node, nil
default:
return nil, fmt.Errorf("cannot resove value of type %v", k)
}
}
func ToStringArray(a values.Array) ([]string, error) {
if a.Type().ElementType() != semantic.String {
return nil, fmt.Errorf("cannot convert array of %v to an array of strings", a.Type().ElementType())
}
strs := make([]string, a.Len())
a.Range(func(i int, v values.Value) {
strs[i] = v.Str()
})
return strs, nil
}
// Arguments provides access to the keyword arguments passed to a function.
// semantic.The Get{Type} methods return three values: the typed value of the arg,
// whether the argument was specified and any errors about the argument type.
// semantic.The GetRequired{Type} methods return only two values, the typed value of the arg and any errors, a missing argument is considered an error in this case.
type Arguments interface {
GetAll() []string
Get(name string) (values.Value, bool)
GetRequired(name string) (values.Value, error)
GetString(name string) (string, bool, error)
GetInt(name string) (int64, bool, error)
GetFloat(name string) (float64, bool, error)
GetBool(name string) (bool, bool, error)
GetFunction(name string) (values.Function, bool, error)
GetArray(name string, t semantic.Kind) (values.Array, bool, error)
GetObject(name string) (values.Object, bool, error)
GetRequiredString(name string) (string, error)
GetRequiredInt(name string) (int64, error)
GetRequiredFloat(name string) (float64, error)
GetRequiredBool(name string) (bool, error)
GetRequiredFunction(name string) (values.Function, error)
GetRequiredArray(name string, t semantic.Kind) (values.Array, error)
GetRequiredObject(name string) (values.Object, error)
// listUnused returns the list of provided arguments that were not used by the function.
listUnused() []string
}
type arguments struct {
obj values.Object
used map[string]bool
}
func newArguments(obj values.Object) *arguments {
if obj == nil {
return new(arguments)
}
return &arguments{
obj: obj,
used: make(map[string]bool, obj.Len()),
}
}
func NewArguments(obj values.Object) Arguments {
return newArguments(obj)
}
func (a *arguments) GetAll() []string {
args := make([]string, 0, a.obj.Len())
a.obj.Range(func(name string, v values.Value) {
args = append(args, name)
})
return args
}
func (a *arguments) Get(name string) (values.Value, bool) {
a.used[name] = true
v, ok := a.obj.Get(name)
return v, ok
}
func (a *arguments) GetRequired(name string) (values.Value, error) {
a.used[name] = true
v, ok := a.obj.Get(name)
if !ok {
return nil, fmt.Errorf("missing required keyword argument %q", name)
}
return v, nil
}
func (a *arguments) GetString(name string) (string, bool, error) {
v, ok, err := a.get(name, semantic.String, false)
if err != nil || !ok {
return "", ok, err
}
return v.Str(), ok, nil
}
func (a *arguments) GetRequiredString(name string) (string, error) {
v, _, err := a.get(name, semantic.String, true)
if err != nil {
return "", err
}
return v.Str(), nil
}
func (a *arguments) GetInt(name string) (int64, bool, error) {
v, ok, err := a.get(name, semantic.Int, false)
if err != nil || !ok {
return 0, ok, err
}
return v.Int(), ok, nil
}
func (a *arguments) GetRequiredInt(name string) (int64, error) {
v, _, err := a.get(name, semantic.Int, true)
if err != nil {
return 0, err
}
return v.Int(), nil
}
func (a *arguments) GetFloat(name string) (float64, bool, error) {
v, ok, err := a.get(name, semantic.Float, false)
if err != nil || !ok {
return 0, ok, err
}
return v.Float(), ok, nil
}
func (a *arguments) GetRequiredFloat(name string) (float64, error) {
v, _, err := a.get(name, semantic.Float, true)
if err != nil {
return 0, err
}
return v.Float(), nil
}
func (a *arguments) GetBool(name string) (bool, bool, error) {
v, ok, err := a.get(name, semantic.Bool, false)
if err != nil || !ok {
return false, ok, err
}
return v.Bool(), ok, nil
}
func (a *arguments) GetRequiredBool(name string) (bool, error) {
v, _, err := a.get(name, semantic.Bool, true)
if err != nil {
return false, err
}
return v.Bool(), nil
}
func (a *arguments) GetArray(name string, t semantic.Kind) (values.Array, bool, error) {
v, ok, err := a.get(name, semantic.Array, false)
if err != nil || !ok {
return nil, ok, err
}
arr := v.Array()
if arr.Type().ElementType() != t {
return nil, true, fmt.Errorf("keyword argument %q should be of an array of type %v, but got an array of type %v", name, t, arr.Type())
}
return v.Array(), ok, nil
}
func (a *arguments) GetRequiredArray(name string, t semantic.Kind) (values.Array, error) {
v, _, err := a.get(name, semantic.Array, true)
if err != nil {
return nil, err
}
arr := v.Array()
if arr.Type().ElementType() != t {
return nil, fmt.Errorf("keyword argument %q should be of an array of type %v, but got an array of type %v", name, t, arr.Type())
}
return arr, nil
}
func (a *arguments) GetFunction(name string) (values.Function, bool, error) {
v, ok, err := a.get(name, semantic.Function, false)
if err != nil || !ok {
return nil, ok, err
}
return v.Function(), ok, nil
}
func (a *arguments) GetRequiredFunction(name string) (values.Function, error) {
v, _, err := a.get(name, semantic.Function, true)
if err != nil {
return nil, err
}
return v.Function(), nil
}
func (a *arguments) GetObject(name string) (values.Object, bool, error) {
v, ok, err := a.get(name, semantic.Object, false)
if err != nil || !ok {
return nil, ok, err
}
return v.Object(), ok, nil
}
func (a *arguments) GetRequiredObject(name string) (values.Object, error) {
v, _, err := a.get(name, semantic.Object, true)
if err != nil {
return nil, err
}
return v.Object(), nil
}
func (a *arguments) get(name string, kind semantic.Kind, required bool) (values.Value, bool, error) {
a.used[name] = true
v, ok := a.obj.Get(name)
if !ok {
if required {
return nil, false, fmt.Errorf("missing required keyword argument %q", name)
}
return nil, false, nil
}
if v.Type().Kind() != kind {
return nil, true, fmt.Errorf("keyword argument %q should be of kind %v, but got %v", name, kind, v.Type().Kind())
}
return v, true, nil
}
func (a *arguments) listUnused() []string {
var unused []string
if a.obj != nil {
a.obj.Range(func(k string, v values.Value) {
if !a.used[k] {
unused = append(unused, k)
}
})
}
return unused
}