package interp
import (
"context"
"go/ast"
"go/token"
"os"
"reflect"
"runtime"
"runtime/debug"
)
type Program struct {
pkgName string
root *node
init []*node
}
func (p *Program ) PackageName () string {
return p .pkgName
}
func (interp *Interpreter ) FileSet () *token .FileSet {
return interp .fset
}
func (interp *Interpreter ) Compile (src string ) (*Program , error ) {
return interp .compileSrc (src , "" , true )
}
func (interp *Interpreter ) CompilePath (path string ) (*Program , error ) {
if !isFile (interp .filesystem , path ) {
_ , err := interp .importSrc (mainID , path , NoTest )
return nil , err
}
b , err := os .ReadFile (path )
if err != nil {
return nil , err
}
return interp .compileSrc (string (b ), path , false )
}
func (interp *Interpreter ) compileSrc (src , name string , inc bool ) (*Program , error ) {
if name != "" {
interp .name = name
}
if interp .name == "" {
interp .name = DefaultSourceName
}
n , err := interp .parse (src , interp .name , inc )
if err != nil {
return nil , err
}
return interp .CompileAST (n )
}
func (interp *Interpreter ) CompileAST (n ast .Node ) (*Program , error ) {
pkgName , root , err := interp .ast (n )
if err != nil || root == nil {
return nil , err
}
if interp .astDot {
dotCmd := interp .dotCmd
if dotCmd == "" {
dotCmd = defaultDotCmd (interp .name , "yaegi-ast-" )
}
root .astDot (dotWriter (dotCmd ), interp .name )
if interp .noRun {
return nil , err
}
}
if err = interp .gtaRetry ([]*node {root }, pkgName , pkgName ); err != nil {
return nil , err
}
initNodes , err := interp .cfg (root , nil , pkgName , pkgName )
if err != nil {
if interp .cfgDot {
dotCmd := interp .dotCmd
if dotCmd == "" {
dotCmd = defaultDotCmd (interp .name , "yaegi-cfg-" )
}
root .cfgDot (dotWriter (dotCmd ))
}
return nil , err
}
if root .kind != fileStmt {
setExec (root .start )
}
interp .mutex .Lock ()
gs := interp .scopes [pkgName ]
if interp .universe .sym [pkgName ] == nil {
interp .srcPkg [pkgName ] = gs .sym
interp .universe .sym [pkgName ] = &symbol {kind : pkgSym , typ : &itype {cat : srcPkgT , path : pkgName }}
interp .pkgNames [pkgName ] = pkgName
}
interp .mutex .Unlock ()
if m := gs .sym [mainID ]; pkgName == mainID && m != nil {
initNodes = append (initNodes , m .node )
}
if interp .cfgDot {
dotCmd := interp .dotCmd
if dotCmd == "" {
dotCmd = defaultDotCmd (interp .name , "yaegi-cfg-" )
}
root .cfgDot (dotWriter (dotCmd ))
}
return &Program {pkgName , root , initNodes }, nil
}
func (interp *Interpreter ) Execute (p *Program ) (res reflect .Value , err error ) {
defer func () {
r := recover ()
if r != nil {
var pc [64 ]uintptr
n := runtime .Callers (1 , pc [:])
err = Panic {Value : r , Callers : pc [:n ], Stack : debug .Stack ()}
}
}()
if err = genRun (p .root ); err != nil {
return res , err
}
interp .frame .setrunid (interp .runid ())
interp .frame .mutex .Lock ()
interp .resizeFrame ()
interp .frame .mutex .Unlock ()
interp .run (p .root , nil )
n , err := genGlobalVars ([]*node {p .root }, interp .scopes [p .pkgName ])
if err != nil {
return res , err
}
interp .run (n , nil )
for _ , n := range p .init {
interp .run (n , interp .frame )
}
v := genValue (p .root )
res = v (interp .frame )
if res .IsValid () {
if n , ok := res .Interface ().(*node ); ok {
res = genFunctionWrapper (n )(interp .frame )
}
}
return res , err
}
func (interp *Interpreter ) ExecuteWithContext (ctx context .Context , p *Program ) (res reflect .Value , err error ) {
interp .mutex .Lock ()
interp .done = make (chan struct {})
interp .cancelChan = !interp .opt .fastChan
interp .mutex .Unlock ()
done := make (chan struct {})
go func () {
defer close (done )
res , err = interp .Execute (p )
}()
select {
case <- ctx .Done ():
interp .stop ()
return reflect .Value {}, ctx .Err ()
case <- done :
}
return res , err
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .