Source file src/simd/_gen/unify/trace.go

     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 envSet
    44  	res   *Value // Unification result
    45  	env   envSet
    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 envSet) {
   131  	if t == nil {
   132  		return
   133  	}
   134  
   135  	t.enc.e = e // Interpret values w.r.t. 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