blob: df4b28facf56b01b47af76dd08d89e86dc0ac956 [file] [log] [blame]
Akron0d9117c2025-05-27 15:20:21 +02001package ast
2
3import (
4 "reflect"
5)
6
Akron13e86462025-07-03 13:44:16 +02007// NodesEqual compares two AST nodes for structural equality, ignoring rewrites metadata
Akron0d9117c2025-05-27 15:20:21 +02008func NodesEqual(a, b Node) bool {
9 if a == nil || b == nil {
10 return a == b
11 }
12
13 if a.Type() != b.Type() {
14 return false
15 }
16
17 switch n1 := a.(type) {
18 case *Term:
19 if n2, ok := b.(*Term); ok {
20 return n1.Foundry == n2.Foundry &&
21 n1.Key == n2.Key &&
22 n1.Layer == n2.Layer &&
23 n1.Match == n2.Match &&
Akron13e86462025-07-03 13:44:16 +020024 n1.Value == n2.Value
Akron0d9117c2025-05-27 15:20:21 +020025 }
26 case *TermGroup:
27 if n2, ok := b.(*TermGroup); ok {
28 if n1.Relation != n2.Relation || len(n1.Operands) != len(n2.Operands) {
29 return false
30 }
31 for i := range n1.Operands {
32 if !NodesEqual(n1.Operands[i], n2.Operands[i]) {
33 return false
34 }
35 }
Akron13e86462025-07-03 13:44:16 +020036 return true
Akron0d9117c2025-05-27 15:20:21 +020037 }
38 case *Token:
39 if n2, ok := b.(*Token); ok {
Akron13e86462025-07-03 13:44:16 +020040 return NodesEqual(n1.Wrap, n2.Wrap)
Akron0d9117c2025-05-27 15:20:21 +020041 }
42 case *CatchallNode:
43 if n2, ok := b.(*CatchallNode); ok {
Akron441bd122025-05-30 14:19:50 +020044 if n1.NodeType != n2.NodeType ||
45 !reflect.DeepEqual(n1.RawContent, n2.RawContent) ||
46 !NodesEqual(n1.Wrap, n2.Wrap) {
47 return false
48 }
49 // Compare operands
50 if len(n1.Operands) != len(n2.Operands) {
51 return false
52 }
53 for i := range n1.Operands {
54 if !NodesEqual(n1.Operands[i], n2.Operands[i]) {
55 return false
56 }
57 }
58 return true
59 }
60 case *Rewrite:
61 if n2, ok := b.(*Rewrite); ok {
62 return n1.Editor == n2.Editor &&
63 n1.Operation == n2.Operation &&
64 n1.Scope == n2.Scope &&
65 n1.Src == n2.Src &&
66 n1.Comment == n2.Comment &&
67 reflect.DeepEqual(n1.Original, n2.Original)
Akron0d9117c2025-05-27 15:20:21 +020068 }
69 }
70 return false
71}
72
Akron0d9117c2025-05-27 15:20:21 +020073// IsTermNode checks if a node is a Term node
74func IsTermNode(node Node) bool {
75 _, ok := node.(*Term)
76 return ok
77}