Files
moxa/interp/ast.go
2018-07-24 16:32:08 +02:00

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
}