<commit_before>package zcl

import (
	"fmt"

	"github.com/zclconf/go-cty/cty"
)

// A Traversal is a description of traversing through a value through a
// series of operations such as attribute lookup, index lookup, etc.
//
// It is used to look up values in scopes, for example.
//
// The traversal operations are implementations of interface Traverser.
// This is a closed set of implementations, so the interface cannot be
// implemented from outside this package.
//
// A traversal can be absolute (its first value is a symbol name) or relative
// (starts from an existing value).
type Traversal []Traverser

// TraversalJoin appends a relative traversal to an absolute traversal to
// produce a new absolute traversal.
func TraversalJoin(abs Traversal, rel Traversal) Traversal {
	if abs.IsRelative() {
		panic("first argument to TraversalJoin must be absolute")
	}
	if !rel.IsRelative() {
		panic("second argument to TraversalJoin must be relative")
	}

	ret := make(Traversal, len(abs)+len(rel))
	copy(ret, abs)
	copy(ret[len(abs):], rel)
	return ret
}

// TraverseRel applies the receiving traversal to the given value, returning
// the resulting value. This is supported only for relative traversals,
// and will panic if applied to an absolute traversal.
func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
	if !t.IsRelative() {
		panic("can't use TraverseRel on an absolute traversal")
	}

	current := val
	var diags Diagnostics
	for _, tr := range t {
		var newDiags Diagnostics
		current, newDiags = tr.TraversalStep(current)
		diags = append(diags, newDiags...)
		if newDiags.HasErrors() {
			return cty.DynamicVal, diags
		}
	}
	return current, diags
}

// TraverseAbs applies the receiving traversal to the given eval context,
// returning the resulting value. This is supported only for absolute
// traversals, and will panic if applied to a relative traversal.
func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) {
	if t.IsRelative() {
		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
// otherwise.
func (t Traversal) IsRelative() bool {
	if len(t) == 0 {<commit_msg>zcl: TraverseAbs through parent scopes<commit_after>package zcl

import (
	"fmt"

	"github.com/zclconf/go-cty/cty"
)

// A Traversal is a description of traversing through a value through a
// series of operations such as attribute lookup, index lookup, etc.
//
// It is used to look up values in scopes, for example.
//
// The traversal operations are implementations of interface Traverser.
// This is a closed set of implementations, so the interface cannot be
// implemented from outside this package.
//
// A traversal can be absolute (its first value is a symbol name) or relative
// (starts from an existing value).
type Traversal []Traverser

// TraversalJoin appends a relative traversal to an absolute traversal to
// produce a new absolute traversal.
func TraversalJoin(abs Traversal, rel Traversal) Traversal {
	if abs.IsRelative() {
		panic("first argument to TraversalJoin must be absolute")
	}
	if !rel.IsRelative() {
		panic("second argument to TraversalJoin must be relative")
	}

	ret := make(Traversal, len(abs)+len(rel))
	copy(ret, abs)
	copy(ret[len(abs):], rel)
	return ret
}

// TraverseRel applies the receiving traversal to the given value, returning
// the resulting value. This is supported only for relative traversals,
// and will panic if applied to an absolute traversal.
func (t Traversal) TraverseRel(val cty.Value) (cty.Value, Diagnostics) {
	if !t.IsRelative() {
		panic("can't use TraverseRel on an absolute traversal")
	}

	current := val
	var diags Diagnostics
	for _, tr := range t {
		var newDiags Diagnostics
		current, newDiags = tr.TraversalStep(current)
		diags = append(diags, newDiags...)
		if newDiags.HasErrors() {
			return cty.DynamicVal, diags
		}
	}
	return current, diags
}

// TraverseAbs applies the receiving traversal to the given eval context,
// returning the resulting value. This is supported only for absolute
// traversals, and will panic if applied to a relative traversal.
func (t Traversal) TraverseAbs(ctx *EvalContext) (cty.Value, Diagnostics) {
	if t.IsRelative() {
		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,
			},
		}
	}

	thisCtx := ctx
	for thisCtx != nil {
		val, exists := thisCtx.Variables[name]
		if exists {
			return split.Rel.TraverseRel(val)
		}
		thisCtx = thisCtx.parent
	}

	suggestions := make([]string, 0, len(ctx.Variables))
	thisCtx = ctx
	for thisCtx != nil {
		for k := range thisCtx.Variables {
			suggestions = append(suggestions, k)
		}
		thisCtx = thisCtx.parent
	}
	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,
		},
	}
}

// IsRelative returns true if the receiver is a relative traversal, or false
// otherwise.
func (t Traversal) IsRelative() bool {
	if len(t) == 0 {
{'fraction_non_alphanumeric': 0.10705937353903694, 'fraction_numerical': 0.0009350163627863488, 'mean_word_length': 3.764662212323682, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7011140', 'n_tokens_mistral': 2153, 'n_tokens_neox': 1982, 'n_words': 718}