1
2
3
4
5 package unify
6
7 import (
8 "fmt"
9 "io"
10 "strings"
11
12 "gopkg.in/yaml.v3"
13 )
14
15
16
17 const debugDotInHTML = false
18
19 var Debug struct {
20
21 UnifyLog io.Writer
22
23
24 HTML io.Writer
25 }
26
27 type tracer struct {
28 logw io.Writer
29
30 enc yamlEncoder
31
32 saveTree bool
33
34 path []string
35
36 node *traceTree
37 trees []*traceTree
38 }
39
40 type traceTree struct {
41 label string
42 v, w *Value
43 envIn envSet
44 res *Value
45 env envSet
46 err 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
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
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 envSet) {
131 if t == nil {
132 return
133 }
134
135 t.enc.e = e
136 t.logf("Unify\n%s\nwith\n%s\nin\n%s",
137 yamlf(" ", t.enc.value(v)),
138 yamlf(" ", t.enc.value(w)),
139 yamlf(" ", t.enc.env(e)))
140 t.enc.e = envSet{}
141
142 if t.saveTree {
143 if t.node == nil {
144 t.node = &traceTree{}
145 t.trees = append(t.trees, t.node)
146 }
147 t.node.v, t.node.w, t.node.envIn = v, w, e
148 }
149 }
150
151 func (t *tracer) traceDone(res *Value, e envSet, err error) {
152 if t == nil {
153 return
154 }
155
156 if err != nil {
157 t.logf("==> %s", err)
158 } else {
159 t.logf("==>\n%s", yamlf(" ", t.enc.closure(Closure{res, e})))
160 }
161
162 if t.saveTree {
163 node := t.node
164 if node == nil {
165 panic("popped top of trace stack")
166 }
167 node.res, node.err = res, err
168 node.env = e
169 }
170 }
171
View as plain text