zcl: Implement Traversal.TraverseAbs
This commit is contained in:
parent
ace387f5f9
commit
68aa56c795
24
zcl/didyoumean.go
Normal file
24
zcl/didyoumean.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package zcl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/agext/levenshtein"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nameSuggestion tries to find a name from the given slice of suggested names
|
||||||
|
// that is close to the given name and returns it if found. If no suggestion
|
||||||
|
// is close enough, returns the empty string.
|
||||||
|
//
|
||||||
|
// The suggestions are tried in order, so earlier suggestions take precedence
|
||||||
|
// if the given string is similar to two or more suggestions.
|
||||||
|
//
|
||||||
|
// This function is intended to be used with a relatively-small number of
|
||||||
|
// suggestions. It's not optimized for hundreds or thousands of them.
|
||||||
|
func nameSuggestion(given string, suggestions []string) string {
|
||||||
|
for _, suggestion := range suggestions {
|
||||||
|
dist := levenshtein.Distance(given, suggestion, nil)
|
||||||
|
if dist < 3 { // threshold determined experimentally
|
||||||
|
return suggestion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
@ -39,6 +39,10 @@ func TraversalJoin(abs Traversal, rel Traversal) Traversal {
|
|||||||
// the resulting value. This is supported only for relative traversals,
|
// the resulting value. This is supported only for relative traversals,
|
||||||
// and will panic if applied to an absolute traversal.
|
// and will panic if applied to an absolute traversal.
|
||||||
func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
|
func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
|
||||||
|
if !t.IsRelative() {
|
||||||
|
panic("can't use TraverseRel on an absolute traversal")
|
||||||
|
}
|
||||||
|
|
||||||
current := val
|
current := val
|
||||||
var diags Diagnostics
|
var diags Diagnostics
|
||||||
for _, tr := range t {
|
for _, tr := range t {
|
||||||
@ -56,8 +60,47 @@ func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
|
|||||||
// returning the resulting value. This is supported only for absolute
|
// returning the resulting value. This is supported only for absolute
|
||||||
// traversals, and will panic if applied to a relative traversal.
|
// traversals, and will panic if applied to a relative traversal.
|
||||||
func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) {
|
func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) {
|
||||||
// TODO: implement
|
if t.IsRelative() {
|
||||||
return cty.DynamicVal, nil
|
panic("can't use TraverseAbs on a relative traversal")
|
||||||
|
}
|
||||||
|
|
||||||
|
split := t.SimpleSplit()
|
||||||
|
root := split.Abs[0].(TraverseRoot)
|
||||||
|
name := root.Name
|
||||||
|
|
||||||
|
if ctx == nil || ctx.Variables == nil {
|
||||||
|
return cty.DynamicVal, Diagnostics{
|
||||||
|
{
|
||||||
|
Severity: DiagError,
|
||||||
|
Summary: "Variables not allowed",
|
||||||
|
Detail: "Variables may not be used here.",
|
||||||
|
Subject: &root.SrcRange,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val, exists := ctx.Variables[name]
|
||||||
|
if !exists {
|
||||||
|
suggestions := make([]string, 0, len(ctx.Variables))
|
||||||
|
for k := range ctx.Variables {
|
||||||
|
suggestions = append(suggestions, k)
|
||||||
|
}
|
||||||
|
suggestion := nameSuggestion(name, suggestions)
|
||||||
|
if suggestion != "" {
|
||||||
|
suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.DynamicVal, Diagnostics{
|
||||||
|
{
|
||||||
|
Severity: DiagError,
|
||||||
|
Summary: "Unknown variable",
|
||||||
|
Detail: fmt.Sprintf("There is no variable named %q.%s", name, suggestion),
|
||||||
|
Subject: &root.SrcRange,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return split.Rel.TraverseRel(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRelative returns true if the receiver is a relative traversal, or false
|
// IsRelative returns true if the receiver is a relative traversal, or false
|
||||||
@ -67,10 +110,10 @@ func (t Traversal) IsRelative() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, firstIsRoot := t[0].(TraverseRoot); firstIsRoot {
|
if _, firstIsRoot := t[0].(TraverseRoot); firstIsRoot {
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// SimpleSplit returns a TraversalSplit where the name lookup is the absolute
|
// SimpleSplit returns a TraversalSplit where the name lookup is the absolute
|
||||||
// part and the remainder is the relative part. Supported only for
|
// part and the remainder is the relative part. Supported only for
|
||||||
|
Loading…
x
Reference in New Issue
Block a user