diff --git a/README.md b/README.md index 70bacca4..575820c8 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,8 @@ test Documentation about Yaegi commands and libraries can be found at usual [godoc.org][docs]. +Key documentation of the internal design: https://marc.vertes.org/yaegi-internals/ Also see [interp/trace.go](interp/trace.go) for helpful printing commands to see what is happening under the hood during compilation. + ## Limitations Beside the known [bugs] which are supposed to be fixed in the short term, there are some limitations not planned to be addressed soon: diff --git a/interp/cfg.go b/interp/cfg.go index 90db9a54..9175c06e 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -74,6 +74,8 @@ func (interp *Interpreter) cfg(root *node, sc *scope, importPath, pkgName string if n.scope == nil { n.scope = sc } + tracePrintln(n) + switch n.kind { case binaryExpr, unaryExpr, parenExpr: if isBoolAction(n) { diff --git a/interp/trace.go b/interp/trace.go new file mode 100644 index 00000000..44cb66fb --- /dev/null +++ b/interp/trace.go @@ -0,0 +1,118 @@ +package interp + +import ( + "fmt" + "reflect" + "strings" +) + +// Set trace to true for debugging the cfg and other processes. +var trace = false + +func traceIndent(n *node) string { + return strings.Repeat(" ", n.depth()) +} + +// tracePrintln works like fmt.Println, with indenting by depth +// and key info on given node. +func tracePrintln(n *node, v ...any) { + if !trace { + return + } + fmt.Println(append([]any{traceIndent(n), n}, v...)...) +} + +// tracePrintTree is particularly useful in post-order for seeing the full +// structure of a given code segment of interest. +// +//nolint:unused // debugging facility +func tracePrintTree(n *node, v ...any) { + if !trace { + return + } + tracePrintln(n, v...) + n.Walk(func(n *node) bool { + tracePrintln(n) + return true + }, nil) +} + +// nodeAddr returns the pointer address of node, short version. +func ptrAddr(v any) string { + p := fmt.Sprintf("%p", v) + return p[:2] + p[9:] // unique bits +} + +// valString returns string rep of given value, showing underlying pointers etc. +// +//nolint:unused // debugging facility +func valString(v reflect.Value) string { + s := v.String() + if v.Kind() == reflect.Func || v.Kind() == reflect.Map || v.Kind() == reflect.Pointer || v.Kind() == reflect.Slice || v.Kind() == reflect.UnsafePointer { + p := fmt.Sprintf("%#x", v.Pointer()) + ln := len(p) + s += " " + p[:2] + p[max(2, ln-4):] + } + return s +} + +func (n *node) String() string { + s := n.kind.String() + if n.ident != "" { + s += " " + n.ident + } + s += " " + ptrAddr(n) + if n.sym != nil { + s += " sym:" + n.sym.String() + } else if n.typ != nil { + s += " typ:" + n.typ.String() + } + if n.findex >= 0 { + s += fmt.Sprintf(" fidx: %d lev: %d", n.findex, n.level) + } + if n.start != nil && n.start != n { + s += fmt.Sprintf(" ->start: %s %s", n.start.kind.String(), ptrAddr(n.start)) + } + if n.tnext != nil { + s += fmt.Sprintf(" ->tnext: %s %s", n.tnext.kind.String(), ptrAddr(n.tnext)) + } + if n.fnext != nil { + s += fmt.Sprintf(" ->fnext: %s %s", n.fnext.kind.String(), ptrAddr(n.fnext)) + } + return s +} + +func (n *node) depth() int { + if n.anc != nil { + return n.anc.depth() + 1 + } + return 0 +} + +func (sy *symbol) String() string { + s := sy.kind.String() + if sy.typ != nil { + s += " (" + sy.typ.String() + ")" + } + if sy.rval.IsValid() { + s += " = " + sy.rval.String() + } + if sy.index >= 0 { + s += fmt.Sprintf(" idx: %d", sy.index) + } + if sy.node != nil { + s += " " + sy.node.String() + } + return s +} + +func (t *itype) String() string { + if t.str != "" { + return t.str + } + s := t.cat.String() + if t.name != "" { + s += " (" + t.name + ")" + } + return s +}