zclsyntax: look in parent EvalContexts for functions
If we can't find a function in the given EvalContext, we must traverse the context chain until either we run out of contexts or we find a matching function. At present there is no reason for a non-root context to have any functions, so this will always traverse to the root. This may change in future if we introduce constructs that define local functions.
This commit is contained in:
parent
4e18e3a8a8
commit
b2e6e2d0d0
@ -17,3 +17,9 @@ type EvalContext struct {
|
||||
func (ctx *EvalContext) NewChild() *EvalContext {
|
||||
return &EvalContext{parent: ctx}
|
||||
}
|
||||
|
||||
// Parent returns the parent of the receiver, or nil if the receiver has
|
||||
// no parent.
|
||||
func (ctx *EvalContext) Parent() *EvalContext {
|
||||
return ctx.parent
|
||||
}
|
||||
|
@ -121,21 +121,35 @@ func (e *FunctionCallExpr) walkChildNodes(w internalWalkFunc) {
|
||||
func (e *FunctionCallExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
||||
var diags zcl.Diagnostics
|
||||
|
||||
if ctx == nil || ctx.Functions == nil {
|
||||
return cty.DynamicVal, zcl.Diagnostics{
|
||||
{
|
||||
Severity: zcl.DiagError,
|
||||
Summary: "Function calls not allowed",
|
||||
Detail: "Functions may not be called here.",
|
||||
Subject: &e.NameRange,
|
||||
Context: e.Range().Ptr(),
|
||||
},
|
||||
var f function.Function
|
||||
exists := false
|
||||
hasNonNilMap := false
|
||||
thisCtx := ctx
|
||||
for thisCtx != nil {
|
||||
if thisCtx.Functions == nil {
|
||||
thisCtx = thisCtx.Parent()
|
||||
continue
|
||||
}
|
||||
hasNonNilMap = true
|
||||
f, exists = thisCtx.Functions[e.Name]
|
||||
if exists {
|
||||
break
|
||||
}
|
||||
thisCtx = thisCtx.Parent()
|
||||
}
|
||||
|
||||
// FIXME: also need to look in ctx.parent, etc
|
||||
f, exists := ctx.Functions[e.Name]
|
||||
if !exists {
|
||||
if !hasNonNilMap {
|
||||
return cty.DynamicVal, zcl.Diagnostics{
|
||||
{
|
||||
Severity: zcl.DiagError,
|
||||
Summary: "Function calls not allowed",
|
||||
Detail: "Functions may not be called here.",
|
||||
Subject: e.Range().Ptr(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
avail := make([]string, 0, len(ctx.Functions))
|
||||
for name := range ctx.Functions {
|
||||
avail = append(avail, name)
|
||||
|
@ -417,6 +417,18 @@ upper(
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
`{for k, v in {hello: "world"}: upper(k) => upper(v) if k == "hello"}`,
|
||||
&zcl.EvalContext{
|
||||
Functions: map[string]function.Function{
|
||||
"upper": stdlib.UpperFunc,
|
||||
},
|
||||
},
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"HELLO": cty.StringVal("WORLD"),
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
`{for k, v in ["world"]: k => v if k == 0}`,
|
||||
nil,
|
||||
|
Loading…
Reference in New Issue
Block a user