<commit_before>package lang

import (
	"bytes"
	"fmt"
	"sync"

	"github.com/hashicorp/terraform/config/lang/ast"
)

// Engine is the execution engine for this language. It should be configured
// prior to running Execute.
type Engine struct {
	// GlobalScope is the global scope of execution for this engine.
	GlobalScope *Scope

	// SemanticChecks is a list of additional semantic checks that will be run
	// on the tree prior to executing it. The type checker, identifier checker,
	// etc. will be run before these.
	SemanticChecks []SemanticChecker
}

// SemanticChecker is the type that must be implemented to do a
// semantic check on an AST tree. This will be called with the root node.
type SemanticChecker func(ast.Node) error

// Execute executes the given ast.Node and returns its final value, its
// type, and an error if one exists.
func (e *Engine) Execute(root ast.Node) (interface{}, ast.Type, error) {
	// Build our own semantic checks that we always run
	tv := &TypeCheck{Scope: e.GlobalScope}
	ic := &IdentifierCheck{Scope: e.GlobalScope}

	// Build up the semantic checks for execution
	checks := make(
		[]SemanticChecker, len(e.SemanticChecks), len(e.SemanticChecks)+2)
	copy(checks, e.SemanticChecks)
	checks = append(checks, ic.Visit)
	checks = append(checks, tv.Visit)

	// Run the semantic checks
	for _, check := range checks {
		if err := check(root); err != nil {
			return nil, ast.TypeInvalid, err
		}
	}

	// Execute
	v := &executeVisitor{Scope: e.GlobalScope}
	return v.Visit(root)
}

// executeVisitor is the visitor used to do the actual execution of
// a program. Note at this point it is assumed that the types check out
// and the identifiers exist.
type executeVisitor struct {
	Scope *Scope

	stack EngineStack
	err   error
	lock  sync.Mutex
}

func (v *executeVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) {
	v.lock.Lock()
	defer v.lock.Unlock()

	// Run the actual visitor pattern
	root.Accept(v.visit)

	// Get our result and clear out everything else
	var result *ast.LiteralNode
	if v.stack.Len() > 0 {
		result = v.stack.Pop()
	} else {
		result = new(ast.LiteralNode)
	}
	resultErr := v.err

	// Clear everything else so we aren't just dangling
	v.stack.Reset()
	v.err = nil

	return result.Value, result.Type, resultErr
}

func (v *executeVisitor) visit(raw ast.Node) {
	if v.err != nil {
		return
	}

	switch n := raw.(type) {
	case *ast.Call:
		v.visitCall(n)
	case *ast.Concat:
		v.visitConcat(n)
	case *ast.LiteralNode:
		v.visitLiteral(n)
	case *ast.VariableAccess:
		v.visitVariableAccess(n)
	default:
		v.err = fmt.Errorf("unknown node: %#v", raw)
	}
}

func (v *executeVisitor) visitCall(n *ast.Call) {
	// Look up the function in the map
	function, ok := v.Scope.FuncMap[n.Func]
	if !ok {
		v.err = fmt.Errorf("unknown function called: %s", n.Func)
		return
	}

	// The arguments are on the stack in reverse order, so pop them off.
	args := make([]interface{}, len(n.Args))
	for i, _ := range n.Args {
		node := v.stack.Pop()
		args[len(n.Args)-1-i] = node.Value
	}

	// Call the function
	result, err := function.Callback(args)
	if err != nil {
		v.err = fmt.Errorf("%s: %s", n.Func, err)
		return
	}

	// Push the result
	v.stack.Push(&ast.LiteralNode{
		Value: result,
		Type:  function.ReturnType,
	})
}

func (v *executeVisitor) visitConcat(n *ast.Concat) {
	// The expressions should all be on the stack in reverse
	// order. So pop them off, reverse their order, and concatenate.
	nodes := make([]*ast.LiteralNode, 0, len(n.Exprs))
	for range n.Exprs {
		nodes = append(nodes, v.stack.Pop())
	}

	var buf bytes.Buffer
	for i := len(nodes) - 1; i >= 0; i-- {
		buf.WriteString(nodes[i].Value.(string))
	}

	v.stack.Push(&ast.LiteralNode{
		Value: buf.String(),
		Type:  ast.TypeString,
	})
}

func (v *executeVisitor) visitLiteral(n *ast.LiteralNode) {
	v.stack.Push(n)
}

func (v *executeVisitor) visitVariableAccess(n *ast.VariableAccess) {
	// Look up the variable in the map
	variable, ok := v.Scope.VarMap[n.Name]
	if !ok {
		v.err = fmt.Errorf("unknown variable accessed: %s", n.Name)
		return
	}

	v.stack.Push(&ast.LiteralNode{
		Value: variable.Value,
		Type:  variable.Type,
	})
}

// EngineStack is a stack of ast.LiteralNodes that the Engine keeps track
// of during execution. This is currently backed by a dumb slice, but can be
// replaced with a better data structure at some point in the future if this
// turns out to require optimization.
type EngineStack struct {
	stack []*ast.LiteralNode
}

func (s *EngineStack) Len() int {
	return len(s.stack)
}

func (s *EngineStack) Push(n *ast.LiteralNode) {
	s.stack = append(s.stack, n)
}

func (s *EngineStack) Pop() *ast.LiteralNode {<commit_msg>config/lang: call the proper functions on Scope<commit_after>package lang

import (
	"bytes"
	"fmt"
	"sync"

	"github.com/hashicorp/terraform/config/lang/ast"
)

// Engine is the execution engine for this language. It should be configured
// prior to running Execute.
type Engine struct {
	// GlobalScope is the global scope of execution for this engine.
	GlobalScope *Scope

	// SemanticChecks is a list of additional semantic checks that will be run
	// on the tree prior to executing it. The type checker, identifier checker,
	// etc. will be run before these.
	SemanticChecks []SemanticChecker
}

// SemanticChecker is the type that must be implemented to do a
// semantic check on an AST tree. This will be called with the root node.
type SemanticChecker func(ast.Node) error

// Execute executes the given ast.Node and returns its final value, its
// type, and an error if one exists.
func (e *Engine) Execute(root ast.Node) (interface{}, ast.Type, error) {
	// Build our own semantic checks that we always run
	tv := &TypeCheck{Scope: e.GlobalScope}
	ic := &IdentifierCheck{Scope: e.GlobalScope}

	// Build up the semantic checks for execution
	checks := make(
		[]SemanticChecker, len(e.SemanticChecks), len(e.SemanticChecks)+2)
	copy(checks, e.SemanticChecks)
	checks = append(checks, ic.Visit)
	checks = append(checks, tv.Visit)

	// Run the semantic checks
	for _, check := range checks {
		if err := check(root); err != nil {
			return nil, ast.TypeInvalid, err
		}
	}

	// Execute
	v := &executeVisitor{Scope: e.GlobalScope}
	return v.Visit(root)
}

// executeVisitor is the visitor used to do the actual execution of
// a program. Note at this point it is assumed that the types check out
// and the identifiers exist.
type executeVisitor struct {
	Scope *Scope

	stack EngineStack
	err   error
	lock  sync.Mutex
}

func (v *executeVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) {
	v.lock.Lock()
	defer v.lock.Unlock()

	// Run the actual visitor pattern
	root.Accept(v.visit)

	// Get our result and clear out everything else
	var result *ast.LiteralNode
	if v.stack.Len() > 0 {
		result = v.stack.Pop()
	} else {
		result = new(ast.LiteralNode)
	}
	resultErr := v.err

	// Clear everything else so we aren't just dangling
	v.stack.Reset()
	v.err = nil

	return result.Value, result.Type, resultErr
}

func (v *executeVisitor) visit(raw ast.Node) {
	if v.err != nil {
		return
	}

	switch n := raw.(type) {
	case *ast.Call:
		v.visitCall(n)
	case *ast.Concat:
		v.visitConcat(n)
	case *ast.LiteralNode:
		v.visitLiteral(n)
	case *ast.VariableAccess:
		v.visitVariableAccess(n)
	default:
		v.err = fmt.Errorf("unknown node: %#v", raw)
	}
}

func (v *executeVisitor) visitCall(n *ast.Call) {
	// Look up the function in the map
	function, ok := v.Scope.LookupFunc(n.Func)
	if !ok {
		v.err = fmt.Errorf("unknown function called: %s", n.Func)
		return
	}

	// The arguments are on the stack in reverse order, so pop them off.
	args := make([]interface{}, len(n.Args))
	for i, _ := range n.Args {
		node := v.stack.Pop()
		args[len(n.Args)-1-i] = node.Value
	}

	// Call the function
	result, err := function.Callback(args)
	if err != nil {
		v.err = fmt.Errorf("%s: %s", n.Func, err)
		return
	}

	// Push the result
	v.stack.Push(&ast.LiteralNode{
		Value: result,
		Type:  function.ReturnType,
	})
}

func (v *executeVisitor) visitConcat(n *ast.Concat) {
	// The expressions should all be on the stack in reverse
	// order. So pop them off, reverse their order, and concatenate.
	nodes := make([]*ast.LiteralNode, 0, len(n.Exprs))
	for range n.Exprs {
		nodes = append(nodes, v.stack.Pop())
	}

	var buf bytes.Buffer
	for i := len(nodes) - 1; i >= 0; i-- {
		buf.WriteString(nodes[i].Value.(string))
	}

	v.stack.Push(&ast.LiteralNode{
		Value: buf.String(),
		Type:  ast.TypeString,
	})
}

func (v *executeVisitor) visitLiteral(n *ast.LiteralNode) {
	v.stack.Push(n)
}

func (v *executeVisitor) visitVariableAccess(n *ast.VariableAccess) {
	// Look up the variable in the map
	variable, ok := v.Scope.LookupVar(n.Name)
	if !ok {
		v.err = fmt.Errorf("unknown variable accessed: %s", n.Name)
		return
	}

	v.stack.Push(&ast.LiteralNode{
		Value: variable.Value,
		Type:  variable.Type,
	})
}

// EngineStack is a stack of ast.LiteralNodes that the Engine keeps track
// of during execution. This is currently backed by a dumb slice, but can be
// replaced with a better data structure at some point in the future if this
// turns out to require optimization.
type EngineStack struct {
	stack []*ast.LiteralNode
}

func (s *EngineStack) Len() int {
	return len(s.stack)
}

func (s *EngineStack) Push(n *ast.LiteralNode) {
	s.stack = append(s.stack, n)
}

func (s *EngineStack) Pop() *ast.LiteralNode {
{'fraction_non_alphanumeric': 0.12553101104502973, 'fraction_numerical': 0.001274426508071368, 'mean_word_length': 3.5625, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '3267182', 'n_tokens_mistral': 3301, 'n_tokens_neox': 3166, 'n_words': 1070}