151 lines
4.2 KiB
Go
151 lines
4.2 KiB
Go
// Package interp implements a Go interpreter.
|
|
package interp
|
|
|
|
import (
|
|
"log"
|
|
"reflect"
|
|
)
|
|
|
|
// Node structure for AST and CFG
|
|
type Node struct {
|
|
child []*Node // child subtrees (AST)
|
|
anc *Node // ancestor (AST)
|
|
start *Node // entry point in subtree (CFG)
|
|
tnext *Node // true branch successor (CFG)
|
|
fnext *Node // false branch successor (CFG)
|
|
interp *Interpreter // interpreter context
|
|
index int // node index (dot display)
|
|
findex int // index of value in frame or frame size (func def, type def)
|
|
fsize int // number of entries in frame (call expressions)
|
|
flen int // frame length (function definition)
|
|
level int // number of frame indirections to access value
|
|
kind Kind // kind of node
|
|
sym *Symbol // associated symbol
|
|
typ *Type // type of value in frame, or nil
|
|
recv *Node // method receiver node for call, or nil
|
|
frame *Frame // frame pointer, only used for script callbacks from runtime (wrapNode)
|
|
action Action // action
|
|
run Builtin // function to run at CFG execution
|
|
val interface{} // static generic value (CFG execution)
|
|
rval reflect.Value // reflection value to let runtime access interpreter (CFG)
|
|
ident string // set if node is a var or func
|
|
}
|
|
|
|
// Frame contains values for the current execution level
|
|
type Frame struct {
|
|
anc *Frame // ancestor frame (global space)
|
|
data []interface{} // values
|
|
}
|
|
|
|
// NodeMap defines a Map of symbols (const, variables and functions) indexed by names
|
|
type NodeMap map[string]*Node
|
|
|
|
// PkgSrcMap stores package source nodes
|
|
type PkgSrcMap map[string]*NodeMap
|
|
|
|
type PkgCtxMap map[string]PkgContext
|
|
|
|
// SymMap stores executable symbols indexed by name
|
|
type SymMap map[string]interface{}
|
|
|
|
// PkgMap stores package executable symbols
|
|
type PkgMap map[string]*SymMap
|
|
|
|
// ValueMap stores symbols as reflect values
|
|
type ValueMap map[string]reflect.Value
|
|
|
|
// PkgValueMap stores package value maps
|
|
type PkgValueMap map[string]*ValueMap
|
|
|
|
// Opt stores interpreter options
|
|
type Opt struct {
|
|
AstDot bool // display AST graph (debug)
|
|
CfgDot bool // display CFG graph (debug)
|
|
NoRun bool // compile, but do not run
|
|
Entry string // interpreter entry point
|
|
}
|
|
|
|
// Interpreter contains global resources and state
|
|
type Interpreter struct {
|
|
Opt
|
|
Frame *Frame
|
|
types TypeMap
|
|
context PkgCtxMap
|
|
binPkg PkgMap
|
|
Exports PkgMap
|
|
Expval PkgValueMap
|
|
}
|
|
|
|
// Walk traverses AST n in depth first order, call cbin function
|
|
// at node entry and cbout function at node exit.
|
|
func (n *Node) Walk(in func(n *Node) bool, out func(n *Node)) {
|
|
if in != nil && !in(n) {
|
|
return
|
|
}
|
|
for _, child := range n.child {
|
|
child.Walk(in, out)
|
|
}
|
|
if out != nil {
|
|
out(n)
|
|
}
|
|
}
|
|
|
|
// NewInterpreter creates and returns a new interpreter object
|
|
func NewInterpreter(opt Opt) *Interpreter {
|
|
return &Interpreter{
|
|
Opt: opt,
|
|
types: defaultTypes,
|
|
context: make(PkgCtxMap),
|
|
binPkg: make(PkgMap),
|
|
Exports: make(PkgMap),
|
|
Expval: make(PkgValueMap),
|
|
}
|
|
}
|
|
|
|
// AddImport registers a symbol from an imported package to be visible from the interpreter
|
|
func (i *Interpreter) AddImport(pkg string, name string, sym interface{}) {
|
|
if i.binPkg[pkg] == nil {
|
|
s := make(SymMap)
|
|
i.binPkg[pkg] = &s
|
|
}
|
|
(*i.binPkg[pkg])[name] = sym
|
|
}
|
|
|
|
// ImportBin registers symbols contained in pkg map
|
|
func (i *Interpreter) ImportBin(pkg *map[string]*map[string]interface{}) {
|
|
for n, p := range *pkg {
|
|
i.binPkg[n] = (*SymMap)(p)
|
|
}
|
|
}
|
|
|
|
// Eval evaluates Go code represented as a string. It returns a map on
|
|
// current interpreted package exported symbols
|
|
func (i *Interpreter) Eval(src string) string {
|
|
// Parse source to AST
|
|
pkgName, root := i.Ast(src)
|
|
log.Println(pkgName)
|
|
if i.AstDot {
|
|
root.AstDot(DotX())
|
|
}
|
|
|
|
// Annotate AST with CFG infos
|
|
initNodes := i.Cfg(root)
|
|
if entry, ok := i.context[pkgName].NodeMap[i.Entry]; ok {
|
|
initNodes = append(initNodes, entry)
|
|
}
|
|
|
|
if i.CfgDot {
|
|
root.CfgDot(DotX())
|
|
}
|
|
|
|
// Execute CFG
|
|
if !i.NoRun {
|
|
i.Frame = &Frame{data: make([]interface{}, root.fsize)}
|
|
runCfg(root.start, i.Frame)
|
|
for _, n := range initNodes {
|
|
Run(n, i.Frame, nil, nil, nil, nil, true, false)
|
|
}
|
|
}
|
|
return root.child[0].ident
|
|
}
|