golang.org/x/arch@v0.17.0/internal/unify/trace.go (about) 1 // Copyright 2025 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package unify 6 7 import ( 8 "fmt" 9 "io" 10 "strings" 11 12 "gopkg.in/yaml.v3" 13 ) 14 15 // debugDotInHTML, if true, includes dot code for all graphs in the HTML. Useful 16 // for debugging the dot output itself. 17 const debugDotInHTML = false 18 19 var Debug struct { 20 // UnifyLog, if non-nil, receives a streaming text trace of unification. 21 UnifyLog io.Writer 22 23 // HTML, if non-nil, writes an HTML trace of unification to HTML. 24 HTML io.Writer 25 } 26 27 type tracer struct { 28 logw io.Writer 29 30 enc yamlEncoder // Print consistent idents throughout 31 32 saveTree bool // if set, record tree; required for HTML output 33 34 path []string 35 36 node *traceTree 37 trees []*traceTree 38 } 39 40 type traceTree struct { 41 label string // Identifies this node as a child of parent 42 v, w *Value // Unification inputs 43 envIn nonDetEnv 44 res *Value // Unification result 45 env nonDetEnv 46 err error // or error 47 48 parent *traceTree 49 children []*traceTree 50 } 51 52 type tracerExit struct { 53 t *tracer 54 len int 55 node *traceTree 56 } 57 58 func (t *tracer) enter(pat string, vals ...any) tracerExit { 59 if t == nil { 60 return tracerExit{} 61 } 62 63 label := fmt.Sprintf(pat, vals...) 64 65 var p *traceTree 66 if t.saveTree { 67 p = t.node 68 if p != nil { 69 t.node = &traceTree{label: label, parent: p} 70 p.children = append(p.children, t.node) 71 } 72 } 73 74 t.path = append(t.path, label) 75 return tracerExit{t, len(t.path) - 1, p} 76 } 77 78 func (t *tracer) enterVar(id *ident, branch int) tracerExit { 79 if t == nil { 80 return tracerExit{} 81 } 82 83 // Use the tracer's ident printer 84 return t.enter("Var %s br %d", t.enc.idp.unique(id), branch) 85 } 86 87 func (te tracerExit) exit() { 88 if te.t == nil { 89 return 90 } 91 te.t.path = te.t.path[:te.len] 92 te.t.node = te.node 93 } 94 95 func indentf(prefix string, pat string, vals ...any) string { 96 s := fmt.Sprintf(pat, vals...) 97 if len(prefix) == 0 { 98 return s 99 } 100 if !strings.Contains(s, "\n") { 101 return prefix + s 102 } 103 104 indent := prefix 105 if strings.TrimLeft(prefix, " ") != "" { 106 // Prefix has non-space characters in it. Construct an all space-indent. 107 indent = strings.Repeat(" ", len(prefix)) 108 } 109 return prefix + strings.ReplaceAll(s, "\n", "\n"+indent) 110 } 111 112 func yamlf(prefix string, node *yaml.Node) string { 113 b, err := yaml.Marshal(node) 114 if err != nil { 115 return fmt.Sprintf("<marshal failed: %s>", err) 116 } 117 return strings.TrimRight(indentf(prefix, "%s", b), " \n") 118 } 119 120 func (t *tracer) logf(pat string, vals ...any) { 121 if t == nil || t.logw == nil { 122 return 123 } 124 prefix := fmt.Sprintf("[%s] ", strings.Join(t.path, "/")) 125 s := indentf(prefix, pat, vals...) 126 s = strings.TrimRight(s, " \n") 127 fmt.Fprintf(t.logw, "%s\n", s) 128 } 129 130 func (t *tracer) traceUnify(v, w *Value, e nonDetEnv) { 131 if t == nil { 132 return 133 } 134 135 t.logf("Unify\n%s\nwith\n%s\nin\n%s", 136 yamlf(" ", t.enc.value(v)), 137 yamlf(" ", t.enc.value(w)), 138 yamlf(" ", t.enc.env(e))) 139 140 if t.saveTree { 141 if t.node == nil { 142 t.node = &traceTree{} 143 t.trees = append(t.trees, t.node) 144 } 145 t.node.v, t.node.w, t.node.envIn = v, w, e 146 } 147 } 148 149 func (t *tracer) traceDone(res *Value, e nonDetEnv, err error) { 150 if t == nil { 151 return 152 } 153 154 if err != nil { 155 t.logf("==> %s", err) 156 } else { 157 t.logf("==>\n%s", yamlf(" ", t.enc.closure(Closure{res, e}))) 158 } 159 160 if t.saveTree { 161 node := t.node 162 if node == nil { 163 panic("popped top of trace stack") 164 } 165 node.res, node.err = res, err 166 node.env = e 167 } 168 }