677 lines
16 KiB
Go
677 lines
16 KiB
Go
package interp
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"strconv"
|
|
)
|
|
|
|
// Kind defines the kind of AST, i.e. the grammar category
|
|
type Kind uint
|
|
|
|
// Node kinds for the go language
|
|
const (
|
|
Undef Kind = iota
|
|
Address
|
|
ArrayType
|
|
AssignStmt
|
|
AssignXStmt
|
|
BasicLit
|
|
BinaryExpr
|
|
BlockStmt
|
|
BranchStmt
|
|
Break
|
|
CallExpr
|
|
CaseClause
|
|
ChanType
|
|
CompositeLitExpr
|
|
ConstDecl
|
|
Continue
|
|
DeclStmt
|
|
Defer
|
|
Define
|
|
DefineX
|
|
Ellipsis
|
|
ExprStmt
|
|
Fallthrough
|
|
Field
|
|
FieldList
|
|
File
|
|
For0 // for {}
|
|
For1 // for cond {}
|
|
For2 // for init; cond; {}
|
|
For3 // for ; cond; post {}
|
|
For3a // for init; ; post {}
|
|
For4 // for init; cond; post {}
|
|
ForRangeStmt // for range
|
|
FuncDecl
|
|
FuncLit
|
|
FuncType
|
|
Go
|
|
GoStmt
|
|
Goto
|
|
Ident
|
|
If0 // if cond {}
|
|
If1 // if cond {} else {}
|
|
If2 // if init; cond {}
|
|
If3 // if init; cond {} else {}
|
|
ImportDecl
|
|
ImportSpec
|
|
IncDecStmt
|
|
IndexExpr
|
|
InterfaceType
|
|
LandExpr
|
|
LorExpr
|
|
KeyValueExpr
|
|
MapType
|
|
ParenExpr
|
|
RangeStmt
|
|
ReturnStmt
|
|
Rvalue
|
|
SelectorExpr
|
|
SelectorImport
|
|
SelectorSrc
|
|
SendStmt
|
|
SliceExpr
|
|
StarExpr
|
|
StructType
|
|
Switch0 // switch tag {}
|
|
Switch1 // switch init; tag {}
|
|
TypeAssertExpr
|
|
TypeDecl
|
|
TypeSpec
|
|
UnaryExpr
|
|
ValueSpec
|
|
VarDecl
|
|
)
|
|
|
|
var kinds = [...]string{
|
|
Undef: "Undef",
|
|
Address: "Address",
|
|
ArrayType: "ArrayType",
|
|
AssignStmt: "AssignStmt",
|
|
AssignXStmt: "AssignXStmt",
|
|
BasicLit: "BasicLit",
|
|
BinaryExpr: "BinaryExpr",
|
|
BlockStmt: "BlockStmt",
|
|
BranchStmt: "BranchStmt",
|
|
Break: "Break",
|
|
CallExpr: "CallExpr",
|
|
CaseClause: "CaseClause",
|
|
ChanType: "ChanType",
|
|
CompositeLitExpr: "CompositeLitExpr",
|
|
ConstDecl: "ConstDecl",
|
|
Continue: "Continue",
|
|
DeclStmt: "DeclStmt",
|
|
Defer: "Defer",
|
|
Define: "Define",
|
|
DefineX: "DefineX",
|
|
Ellipsis: "Ellipsis",
|
|
ExprStmt: "ExprStmt",
|
|
Field: "Field",
|
|
FieldList: "FieldList",
|
|
File: "File",
|
|
For0: "For0",
|
|
For1: "For1",
|
|
For2: "For2",
|
|
For3: "For3",
|
|
For3a: "For3a",
|
|
For4: "For4",
|
|
ForRangeStmt: "ForRangeStmt",
|
|
FuncDecl: "FuncDecl",
|
|
FuncType: "FuncType",
|
|
FuncLit: "FuncLit",
|
|
Go: "Go",
|
|
GoStmt: "GoStmt",
|
|
Goto: "Goto",
|
|
Ident: "Ident",
|
|
If0: "If0",
|
|
If1: "If1",
|
|
If2: "If2",
|
|
If3: "If3",
|
|
ImportDecl: "ImportDecl",
|
|
ImportSpec: "ImportSpec",
|
|
IncDecStmt: "IncDecStmt",
|
|
IndexExpr: "IndexExpr",
|
|
InterfaceType: "InterfaceType",
|
|
KeyValueExpr: "KeyValueExpr",
|
|
LandExpr: "LandExpr",
|
|
LorExpr: "LorExpr",
|
|
MapType: "MapType",
|
|
ParenExpr: "ParenExpr",
|
|
RangeStmt: "RangeStmt",
|
|
ReturnStmt: "ReturnStmt",
|
|
Rvalue: "Rvalue",
|
|
SelectorExpr: "SelectorExpr",
|
|
SelectorImport: "SelectorImport",
|
|
SelectorSrc: "SelectorSrc",
|
|
SendStmt: "SendStmt",
|
|
SliceExpr: "SliceExpr",
|
|
StarExpr: "StarExpr",
|
|
StructType: "StructType",
|
|
Switch0: "Switch0",
|
|
Switch1: "Switch1",
|
|
TypeAssertExpr: "TypeAssertExpr",
|
|
TypeDecl: "TypeDecl",
|
|
TypeSpec: "TypeSpec",
|
|
UnaryExpr: "UnaryExpr",
|
|
ValueSpec: "ValueSpec",
|
|
VarDecl: "VarDecl",
|
|
}
|
|
|
|
func (k Kind) String() string {
|
|
if k < Kind(len(kinds)) {
|
|
return kinds[k]
|
|
}
|
|
return "Kind(" + strconv.Itoa(int(k)) + ")"
|
|
}
|
|
|
|
// Action defines the node action to perform at execution
|
|
type Action uint
|
|
|
|
// Node actions for the go language
|
|
const (
|
|
Nop Action = iota
|
|
Addr
|
|
ArrayLit
|
|
Assign
|
|
AssignX
|
|
Assign0
|
|
Add
|
|
And
|
|
Call
|
|
CallF
|
|
Case
|
|
CompositeLit
|
|
Dec
|
|
Equal
|
|
Greater
|
|
GetFunc
|
|
GetIndex
|
|
Inc
|
|
Land
|
|
Lor
|
|
Lower
|
|
Mul
|
|
Negate
|
|
Not
|
|
NotEqual
|
|
Quotient
|
|
Range
|
|
Recv
|
|
Remain
|
|
Return
|
|
Send
|
|
Slice
|
|
Slice0
|
|
Star
|
|
Sub
|
|
TypeAssert
|
|
)
|
|
|
|
var actions = [...]string{
|
|
Nop: "nop",
|
|
Addr: "&",
|
|
ArrayLit: "arrayLit",
|
|
Assign: "=",
|
|
AssignX: "X=",
|
|
Assign0: "0=",
|
|
Add: "+",
|
|
And: "&",
|
|
Call: "call",
|
|
CallF: "call",
|
|
Case: "case",
|
|
CompositeLit: "compositeLit",
|
|
Dec: "--",
|
|
Equal: "==",
|
|
Greater: ">",
|
|
GetFunc: "getFunc",
|
|
GetIndex: "getIndex",
|
|
Inc: "++",
|
|
Land: "&&",
|
|
Lor: "||",
|
|
Lower: "<",
|
|
Mul: "*",
|
|
Negate: "-",
|
|
Not: "!",
|
|
NotEqual: "!=",
|
|
Quotient: "/",
|
|
Range: "range",
|
|
Recv: "<-",
|
|
Remain: "%",
|
|
Return: "return",
|
|
Send: "<-",
|
|
Slice: "slice",
|
|
Slice0: "slice0",
|
|
Star: "*",
|
|
Sub: "-",
|
|
TypeAssert: "TypeAssert",
|
|
}
|
|
|
|
func (a Action) String() string {
|
|
if a < Action(len(actions)) {
|
|
return actions[a]
|
|
}
|
|
return "Action(" + strconv.Itoa(int(a)) + ")"
|
|
}
|
|
|
|
// Note: no type analysis is performed at this stage, it is done in pre-order processing
|
|
// of CFG, in order to accomodate forward type declarations
|
|
|
|
// Ast parses src string containing Go code and generates the corresponding AST.
|
|
// The package name and the AST root node are returned.
|
|
func (interp *Interpreter) Ast(src string) (string, *Node) {
|
|
fset := token.NewFileSet() // positions are relative to fset
|
|
f, err := parser.ParseFile(fset, "sample.go", src, 0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
index := 0
|
|
var root, anc *Node
|
|
var st nodestack
|
|
var nbAssign int
|
|
var typeSpec bool
|
|
var pkgName string
|
|
|
|
addChild := func(root **Node, anc *Node, index *int, kind Kind, action Action) *Node {
|
|
*index++
|
|
var i interface{}
|
|
n := &Node{anc: anc, interp: interp, index: *index, kind: kind, action: action, val: &i, run: builtin[action]}
|
|
n.start = n
|
|
if anc == nil {
|
|
*root = n
|
|
} else {
|
|
anc.child = append(anc.child, n)
|
|
if anc.action == Assign && nbAssign > 1 {
|
|
if !typeSpec && len(anc.child) == 2*nbAssign {
|
|
// All LHS and RSH assing child are now defined, so split multiple assign
|
|
// statement into single assign statements.
|
|
newAnc := anc.anc
|
|
newChild := []*Node{}
|
|
for i := 0; i < nbAssign; i++ {
|
|
// set new signle assign
|
|
*index++
|
|
na := &Node{anc: anc.anc, interp: interp, index: *index, kind: anc.kind, action: anc.action, val: new(interface{}), run: anc.run}
|
|
na.start = na
|
|
newChild = append(newChild, na)
|
|
// Set single assign left hand side
|
|
anc.child[i].anc = na
|
|
na.child = append(na.child, anc.child[i])
|
|
// Set single assign right hand side
|
|
anc.child[i+nbAssign].anc = na
|
|
na.child = append(na.child, anc.child[i+nbAssign])
|
|
}
|
|
newAnc.child = newChild
|
|
} else if typeSpec && len(anc.child) == 2*nbAssign+1 {
|
|
// All LHS and RHS assing child are now defined, so split multiple assign
|
|
// statement into single assign statements. Set type for each assignment.
|
|
typeSpec = false
|
|
newAnc := anc.anc
|
|
newChild := []*Node{}
|
|
typeNode := anc.child[nbAssign]
|
|
for i := 0; i < nbAssign; i++ {
|
|
// set new signle assign
|
|
*index++
|
|
na := &Node{anc: anc.anc, interp: interp, index: *index, kind: anc.kind, action: anc.action, val: new(interface{}), run: anc.run}
|
|
na.start = na
|
|
newChild = append(newChild, na)
|
|
// set new type for this assignment
|
|
*index++
|
|
nt := &Node{anc: na, interp: interp, ident: typeNode.ident, index: *index, kind: typeNode.kind, action: typeNode.action, val: new(interface{}), run: typeNode.run}
|
|
// Set single assign left hand side
|
|
anc.child[i].anc = na
|
|
na.child = append(na.child, anc.child[i])
|
|
// Set assignment type
|
|
na.child = append(na.child, nt)
|
|
// Set single assign right hand side
|
|
anc.child[i+nbAssign+1].anc = na
|
|
na.child = append(na.child, anc.child[i+nbAssign+1])
|
|
}
|
|
newAnc.child = newChild
|
|
}
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
|
|
// Populate our own private AST from Go parser AST.
|
|
// A stack of ancestor nodes is used to keep track of curent ancestor for each depth level
|
|
ast.Inspect(f, func(node ast.Node) bool {
|
|
anc = st.top()
|
|
switch a := node.(type) {
|
|
case nil:
|
|
anc = st.pop()
|
|
|
|
case *ast.ArrayType:
|
|
st.push(addChild(&root, anc, &index, ArrayType, Nop))
|
|
|
|
case *ast.AssignStmt:
|
|
var action Action
|
|
var kind Kind
|
|
if len(a.Lhs) > 1 && len(a.Rhs) == 1 {
|
|
if a.Tok == token.DEFINE {
|
|
kind = DefineX
|
|
} else {
|
|
kind = AssignXStmt
|
|
}
|
|
action = AssignX
|
|
} else {
|
|
if a.Tok == token.DEFINE {
|
|
kind = Define
|
|
} else {
|
|
kind = AssignStmt
|
|
}
|
|
action = Assign
|
|
nbAssign = len(a.Lhs)
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, action))
|
|
|
|
case *ast.BasicLit:
|
|
n := addChild(&root, anc, &index, BasicLit, Nop)
|
|
n.ident = a.Value
|
|
switch a.Kind {
|
|
case token.CHAR:
|
|
n.val = a.Value[1]
|
|
case token.FLOAT:
|
|
n.val, _ = strconv.ParseFloat(a.Value, 64)
|
|
case token.IMAG:
|
|
v, _ := strconv.ParseFloat(a.Value[:len(a.Value)-1], 64)
|
|
n.val = complex(0, v)
|
|
case token.INT:
|
|
v, _ := strconv.ParseInt(a.Value, 0, 0)
|
|
n.val = int(v)
|
|
case token.STRING:
|
|
n.val = a.Value[1 : len(a.Value)-1]
|
|
}
|
|
st.push(n)
|
|
|
|
case *ast.BinaryExpr:
|
|
kind := Kind(BinaryExpr)
|
|
action := Action(Nop)
|
|
switch a.Op {
|
|
case token.ADD:
|
|
action = Add
|
|
case token.AND:
|
|
action = And
|
|
case token.EQL:
|
|
action = Equal
|
|
case token.GTR:
|
|
action = Greater
|
|
case token.LAND:
|
|
kind = LandExpr
|
|
action = Land
|
|
case token.LOR:
|
|
kind = LorExpr
|
|
action = Lor
|
|
case token.LSS:
|
|
action = Lower
|
|
case token.MUL:
|
|
action = Mul
|
|
case token.NEQ:
|
|
action = NotEqual
|
|
case token.REM:
|
|
action = Remain
|
|
case token.SUB:
|
|
action = Sub
|
|
case token.QUO:
|
|
action = Quotient
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, action))
|
|
|
|
case *ast.BlockStmt:
|
|
st.push(addChild(&root, anc, &index, BlockStmt, Nop))
|
|
|
|
case *ast.BranchStmt:
|
|
var kind Kind
|
|
switch a.Tok {
|
|
case token.BREAK:
|
|
kind = Break
|
|
case token.CONTINUE:
|
|
kind = Continue
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, Nop))
|
|
|
|
case *ast.CallExpr:
|
|
st.push(addChild(&root, anc, &index, CallExpr, Call))
|
|
|
|
case *ast.CaseClause:
|
|
st.push(addChild(&root, anc, &index, CaseClause, Case))
|
|
|
|
case *ast.ChanType:
|
|
st.push(addChild(&root, anc, &index, ChanType, Nop))
|
|
|
|
case *ast.CompositeLit:
|
|
st.push(addChild(&root, anc, &index, CompositeLitExpr, Nop))
|
|
|
|
case *ast.DeclStmt:
|
|
st.push(addChild(&root, anc, &index, DeclStmt, Nop))
|
|
|
|
case *ast.Ellipsis:
|
|
st.push(addChild(&root, anc, &index, Ellipsis, Nop))
|
|
|
|
case *ast.ExprStmt:
|
|
st.push(addChild(&root, anc, &index, ExprStmt, Nop))
|
|
|
|
case *ast.Field:
|
|
st.push(addChild(&root, anc, &index, Field, Nop))
|
|
|
|
case *ast.FieldList:
|
|
st.push(addChild(&root, anc, &index, FieldList, Nop))
|
|
|
|
case *ast.File:
|
|
pkgName = a.Name.Name
|
|
st.push(addChild(&root, anc, &index, File, Nop))
|
|
|
|
case *ast.ForStmt:
|
|
// Disambiguate variants of FOR statements with a node kind per variant
|
|
var kind Kind
|
|
if a.Cond == nil {
|
|
if a.Init != nil && a.Post != nil {
|
|
kind = For3a
|
|
} else {
|
|
kind = For0
|
|
}
|
|
} else {
|
|
if a.Init == nil && a.Post == nil {
|
|
kind = For1
|
|
} else if a.Init != nil && a.Post == nil {
|
|
kind = For2
|
|
} else if a.Init == nil && a.Post != nil {
|
|
kind = For3
|
|
} else {
|
|
kind = For4
|
|
}
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, Nop))
|
|
|
|
case *ast.FuncDecl:
|
|
n := addChild(&root, anc, &index, FuncDecl, Nop)
|
|
if a.Recv == nil {
|
|
// function is not a method, create an empty receiver list
|
|
addChild(&root, n, &index, FieldList, Nop)
|
|
}
|
|
st.push(n)
|
|
|
|
case *ast.FuncLit:
|
|
n := addChild(&root, anc, &index, FuncLit, GetFunc)
|
|
addChild(&root, n, &index, FieldList, Nop)
|
|
addChild(&root, n, &index, Undef, Nop)
|
|
st.push(n)
|
|
|
|
case *ast.FuncType:
|
|
st.push(addChild(&root, anc, &index, FuncType, Nop))
|
|
|
|
case *ast.GenDecl:
|
|
var kind Kind
|
|
switch a.Tok {
|
|
case token.CONST:
|
|
kind = ConstDecl
|
|
case token.IMPORT:
|
|
kind = ImportDecl
|
|
case token.TYPE:
|
|
kind = TypeDecl
|
|
case token.VAR:
|
|
kind = VarDecl
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, Nop))
|
|
|
|
case *ast.GoStmt:
|
|
st.push(addChild(&root, anc, &index, GoStmt, Nop))
|
|
|
|
case *ast.Ident:
|
|
n := addChild(&root, anc, &index, Ident, Nop)
|
|
n.ident = a.Name
|
|
st.push(n)
|
|
|
|
case *ast.IfStmt:
|
|
// Disambiguate variants of IF statements with a node kind per variant
|
|
var kind Kind
|
|
if a.Init == nil && a.Else == nil {
|
|
kind = If0
|
|
} else if a.Init == nil && a.Else != nil {
|
|
kind = If1
|
|
} else if a.Else == nil {
|
|
kind = If2
|
|
} else {
|
|
kind = If3
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, Nop))
|
|
|
|
case *ast.ImportSpec:
|
|
st.push(addChild(&root, anc, &index, ImportSpec, Nop))
|
|
|
|
case *ast.IncDecStmt:
|
|
var action Action
|
|
switch a.Tok {
|
|
case token.INC:
|
|
action = Inc
|
|
case token.DEC:
|
|
action = Dec
|
|
}
|
|
st.push(addChild(&root, anc, &index, IncDecStmt, action))
|
|
|
|
case *ast.IndexExpr:
|
|
st.push(addChild(&root, anc, &index, IndexExpr, GetIndex))
|
|
|
|
case *ast.InterfaceType:
|
|
st.push(addChild(&root, anc, &index, InterfaceType, Nop))
|
|
|
|
case *ast.KeyValueExpr:
|
|
st.push(addChild(&root, anc, &index, KeyValueExpr, Nop))
|
|
|
|
case *ast.MapType:
|
|
st.push(addChild(&root, anc, &index, MapType, Nop))
|
|
|
|
case *ast.ParenExpr:
|
|
st.push(addChild(&root, anc, &index, ParenExpr, Nop))
|
|
|
|
case *ast.RangeStmt:
|
|
// Insert a missing ForRangeStmt for AST correctness
|
|
n := addChild(&root, anc, &index, ForRangeStmt, Nop)
|
|
st.push(addChild(&root, n, &index, RangeStmt, Range))
|
|
|
|
case *ast.ReturnStmt:
|
|
st.push(addChild(&root, anc, &index, ReturnStmt, Return))
|
|
|
|
case *ast.SelectorExpr:
|
|
st.push(addChild(&root, anc, &index, SelectorExpr, GetIndex))
|
|
|
|
case *ast.SendStmt:
|
|
st.push(addChild(&root, anc, &index, SendStmt, Send))
|
|
|
|
case *ast.SliceExpr:
|
|
if a.Low == nil {
|
|
st.push(addChild(&root, anc, &index, SliceExpr, Slice0))
|
|
} else {
|
|
st.push(addChild(&root, anc, &index, SliceExpr, Slice))
|
|
}
|
|
|
|
case *ast.StarExpr:
|
|
st.push(addChild(&root, anc, &index, StarExpr, Star))
|
|
|
|
case *ast.StructType:
|
|
st.push(addChild(&root, anc, &index, StructType, Nop))
|
|
|
|
case *ast.SwitchStmt:
|
|
if a.Init == nil {
|
|
st.push(addChild(&root, anc, &index, Switch0, Nop))
|
|
} else {
|
|
st.push(addChild(&root, anc, &index, Switch1, Nop))
|
|
}
|
|
|
|
case *ast.TypeAssertExpr:
|
|
st.push(addChild(&root, anc, &index, TypeAssertExpr, TypeAssert))
|
|
|
|
case *ast.TypeSpec:
|
|
st.push(addChild(&root, anc, &index, TypeSpec, Nop))
|
|
|
|
case *ast.UnaryExpr:
|
|
var kind = UnaryExpr
|
|
var action Action
|
|
switch a.Op {
|
|
case token.AND:
|
|
kind = Address
|
|
action = Addr
|
|
case token.ARROW:
|
|
action = Recv
|
|
case token.NOT:
|
|
action = Not
|
|
case token.SUB:
|
|
action = Negate
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, action))
|
|
|
|
case *ast.ValueSpec:
|
|
// TODO: export package global var/const in def
|
|
var kind Kind
|
|
var action Action
|
|
if a.Values != nil {
|
|
if len(a.Names) == 1 && len(a.Values) > 1 {
|
|
kind, action = AssignXStmt, AssignX
|
|
} else {
|
|
kind, action = AssignStmt, Assign
|
|
nbAssign = len(a.Names)
|
|
}
|
|
if a.Type != nil {
|
|
typeSpec = true
|
|
}
|
|
} else if anc.kind == ConstDecl {
|
|
kind, action = AssignStmt, Assign
|
|
} else {
|
|
kind, action = ValueSpec, Assign0
|
|
}
|
|
st.push(addChild(&root, anc, &index, kind, action))
|
|
|
|
default:
|
|
fmt.Printf("Unknown kind for %T\n", a)
|
|
st.push(addChild(&root, anc, &index, Undef, Nop))
|
|
}
|
|
return true
|
|
})
|
|
return pkgName, root
|
|
}
|
|
|
|
type nodestack []*Node
|
|
|
|
func (s *nodestack) push(v *Node) {
|
|
*s = append(*s, v)
|
|
}
|
|
|
|
func (s *nodestack) pop() *Node {
|
|
l := len(*s) - 1
|
|
res := (*s)[l]
|
|
*s = (*s)[:l]
|
|
return res
|
|
}
|
|
|
|
func (s *nodestack) top() *Node {
|
|
l := len(*s)
|
|
if l > 0 {
|
|
return (*s)[l-1]
|
|
}
|
|
return nil
|
|
}
|