Flesh out interface for evaluating expressions

Expressions can now be evaluated within an "EvalContext", which provides
the variable and function scopes. The JSON implementation of this
currently ignores the context entirely and just returns literal values,
since we've not yet implemented the template language parser that would
be needed for the JSON parser to properly support expressions.
This commit is contained in:
Martin Atkins 2017-05-20 15:17:56 -07:00
parent 92e407e672
commit dfafa6fc00
3 changed files with 73 additions and 5 deletions

19
zcl/eval_context.go Normal file
View File

@ -0,0 +1,19 @@
package zcl
import (
"github.com/apparentlymart/go-cty/cty"
"github.com/apparentlymart/go-cty/cty/function"
)
// An EvalContext provides the variables and functions that should be used
// to evaluate an expression.
type EvalContext struct {
Variables map[string]cty.Value
Functions map[string]function.Function
parent *EvalContext
}
// NewChild returns a new EvalContext that is a child of the receiver.
func (ctx *EvalContext) NewChild() *EvalContext {
return &EvalContext{parent: ctx}
}

View File

@ -226,7 +226,36 @@ func (b *body) unpackBlock(v node, typeName string, typeRange *zcl.Range, labels
return
}
func (e *expression) LiteralValue() (cty.Value, zcl.Diagnostics) {
// TODO: Implement
return cty.NilVal, nil
func (e *expression) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
// TEMP: Since we've not yet implemented the zcl native template language
// parser, for the moment we'll support only literal values here.
// FIXME: Once the template language parser is implemented, parse string
// values as templates and evaluate them.
switch v := e.src.(type) {
case *stringVal:
return cty.StringVal(v.Value), nil
case *numberVal:
return cty.NumberVal(v.Value), nil
case *booleanVal:
return cty.BoolVal(v.Value), nil
case *arrayVal:
vals := []cty.Value{}
for _, jsonVal := range v.Values {
val, _ := (&expression{src: jsonVal}).Value(ctx)
vals = append(vals, val)
}
return cty.TupleVal(vals), nil
case *objectVal:
attrs := map[string]cty.Value{}
for name, jsonAttr := range v.Attrs {
val, _ := (&expression{src: jsonAttr.Value}).Value(ctx)
attrs[name] = val
}
return cty.ObjectVal(attrs), nil
default:
// Default to DynamicVal so that ASTs containing invalid nodes can
// still be partially-evaluated.
return cty.DynamicVal, nil
}
}

View File

@ -82,8 +82,28 @@ type Attribute struct {
// Expression is a literal value or an expression provided in the
// configuration, which can be evaluated within a scope to produce a value.
type Expression interface {
LiteralValue() (cty.Value, Diagnostics)
// TODO: evaluation of non-literal expressions
// Value returns the value resulting from evaluating the expression
// in the given evaluation context.
//
// The context may be nil, in which case the expression may contain
// only constants and diagnostics will be produced for any non-constant
// sub-expressions. (The exact definition of this depends on the source
// language.)
//
// The context may instead be set but have either its Variables or
// Functions maps set to nil, in which case only use of these features
// will return diagnostics.
//
// Different diagnostics are provided depending on whether the given
// context maps are nil or empty. In the former case, the message
// tells the user that variables/functions are not permitted at all,
// while in the latter case usage will produce a "not found" error for
// the specific symbol in question.
Value(ctx *EvalContext) (cty.Value, Diagnostics)
// TODO: A "Variables" method that returns a description of all of the
// variables used in the expression, so callers can populate the scope
// only with variables that are actually used.
}
// OfType filters the receiving block sequence by block type name,