package interp

import (
	
	
	
	
	
	
	
)

// A Program is Go code that has been parsed and compiled.
type Program struct {
	pkgName string
	root    *node
	init    []*node
}

// PackageName returns name used in a package clause.
func ( *Program) () string {
	return .pkgName
}

// FileSet is the fileset that must be used for parsing Go that will be passed
// to interp.CompileAST().
func ( *Interpreter) () *token.FileSet {
	return .fset
}

// Compile parses and compiles a Go code represented as a string.
func ( *Interpreter) ( string) (*Program, error) {
	return .compileSrc(, "", true)
}

// CompilePath parses and compiles a Go code located at the given path.
func ( *Interpreter) ( string) (*Program, error) {
	if !isFile(.filesystem, ) {
		,  := .importSrc(mainID, , NoTest)
		return nil, 
	}

	,  := os.ReadFile()
	if  != nil {
		return nil, 
	}
	return .compileSrc(string(), , false)
}

func ( *Interpreter) (,  string,  bool) (*Program, error) {
	if  != "" {
		.name = 
	}
	if .name == "" {
		.name = DefaultSourceName
	}

	// Parse source to AST.
	,  := .parse(, .name, )
	if  != nil {
		return nil, 
	}

	return .CompileAST()
}

// CompileAST builds a Program for the given Go code AST. Files and block
// statements can be compiled, as can most expressions. Var declaration nodes
// cannot be compiled.
//
// WARNING: The node must have been parsed using interp.FileSet(). Results are
// unpredictable otherwise.
func ( *Interpreter) ( ast.Node) (*Program, error) {
	// Convert AST.
	, ,  := .ast()
	if  != nil ||  == nil {
		return nil, 
	}

	if .astDot {
		 := .dotCmd
		if  == "" {
			 = defaultDotCmd(.name, "yaegi-ast-")
		}
		.astDot(dotWriter(), .name)
		if .noRun {
			return nil, 
		}
	}

	// Perform global types analysis.
	if  = .gtaRetry([]*node{}, , );  != nil {
		return nil, 
	}

	// Annotate AST with CFG informations.
	,  := .cfg(, nil, , )
	if  != nil {
		if .cfgDot {
			 := .dotCmd
			if  == "" {
				 = defaultDotCmd(.name, "yaegi-cfg-")
			}
			.cfgDot(dotWriter())
		}
		return nil, 
	}

	if .kind != fileStmt {
		// REPL may skip package statement.
		setExec(.start)
	}
	.mutex.Lock()
	 := .scopes[]
	if .universe.sym[] == nil {
		// Make the package visible under a path identical to its name.
		.srcPkg[] = .sym
		.universe.sym[] = &symbol{kind: pkgSym, typ: &itype{cat: srcPkgT, path: }}
		.pkgNames[] = 
	}
	.mutex.Unlock()

	// Add main to list of functions to run, after all inits.
	if  := .sym[mainID];  == mainID &&  != nil {
		 = append(, .node)
	}

	if .cfgDot {
		 := .dotCmd
		if  == "" {
			 = defaultDotCmd(.name, "yaegi-cfg-")
		}
		.cfgDot(dotWriter())
	}

	return &Program{, , }, nil
}

// Execute executes compiled Go code.
func ( *Interpreter) ( *Program) ( reflect.Value,  error) {
	defer func() {
		 := recover()
		if  != nil {
			var  [64]uintptr // 64 frames should be enough.
			 := runtime.Callers(1, [:])
			 = Panic{Value: , Callers: [:], Stack: debug.Stack()}
		}
	}()

	// Generate node exec closures.
	if  = genRun(.root);  != nil {
		return , 
	}

	// Init interpreter execution memory frame.
	.frame.setrunid(.runid())
	.frame.mutex.Lock()
	.resizeFrame()
	.frame.mutex.Unlock()

	// Execute node closures.
	.run(.root, nil)

	// Wire and execute global vars.
	,  := genGlobalVars([]*node{.root}, .scopes[.pkgName])
	if  != nil {
		return , 
	}
	.run(, nil)

	for ,  := range .init {
		.run(, .frame)
	}
	 := genValue(.root)
	 = (.frame)

	// If result is an interpreter node, wrap it in a runtime callable function.
	if .IsValid() {
		if ,  := .Interface().(*node);  {
			 = genFunctionWrapper()(.frame)
		}
	}

	return , 
}

// ExecuteWithContext executes compiled Go code.
func ( *Interpreter) ( context.Context,  *Program) ( reflect.Value,  error) {
	.mutex.Lock()
	.done = make(chan struct{})
	.cancelChan = !.opt.fastChan
	.mutex.Unlock()

	 := make(chan struct{})
	go func() {
		defer close()
		,  = .Execute()
	}()

	select {
	case <-.Done():
		.stop()
		return reflect.Value{}, .Err()
	case <-:
	}
	return , 
}