blob: 617e8f0549b844e793c872134e106f58f2755fe1 [file] [log] [blame]
Akron0d9117c2025-05-27 15:20:21 +02001package ast
2
3import (
4 "reflect"
5)
6
7// NodesEqual compares two AST nodes for equality
8func 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 &&
Akron441bd122025-05-30 14:19:50 +020024 n1.Value == n2.Value &&
25 rewritesEqual(n1.Rewrites, n2.Rewrites)
Akron0d9117c2025-05-27 15:20:21 +020026 }
27 case *TermGroup:
28 if n2, ok := b.(*TermGroup); ok {
29 if n1.Relation != n2.Relation || len(n1.Operands) != len(n2.Operands) {
30 return false
31 }
32 for i := range n1.Operands {
33 if !NodesEqual(n1.Operands[i], n2.Operands[i]) {
34 return false
35 }
36 }
Akron441bd122025-05-30 14:19:50 +020037 return rewritesEqual(n1.Rewrites, n2.Rewrites)
Akron0d9117c2025-05-27 15:20:21 +020038 }
39 case *Token:
40 if n2, ok := b.(*Token); ok {
Akron441bd122025-05-30 14:19:50 +020041 return NodesEqual(n1.Wrap, n2.Wrap) &&
42 rewritesEqual(n1.Rewrites, n2.Rewrites)
Akron0d9117c2025-05-27 15:20:21 +020043 }
44 case *CatchallNode:
45 if n2, ok := b.(*CatchallNode); ok {
Akron441bd122025-05-30 14:19:50 +020046 if n1.NodeType != n2.NodeType ||
47 !reflect.DeepEqual(n1.RawContent, n2.RawContent) ||
48 !NodesEqual(n1.Wrap, n2.Wrap) {
49 return false
50 }
51 // Compare operands
52 if len(n1.Operands) != len(n2.Operands) {
53 return false
54 }
55 for i := range n1.Operands {
56 if !NodesEqual(n1.Operands[i], n2.Operands[i]) {
57 return false
58 }
59 }
60 return true
61 }
62 case *Rewrite:
63 if n2, ok := b.(*Rewrite); ok {
64 return n1.Editor == n2.Editor &&
65 n1.Operation == n2.Operation &&
66 n1.Scope == n2.Scope &&
67 n1.Src == n2.Src &&
68 n1.Comment == n2.Comment &&
69 reflect.DeepEqual(n1.Original, n2.Original)
Akron0d9117c2025-05-27 15:20:21 +020070 }
71 }
72 return false
73}
74
Akron441bd122025-05-30 14:19:50 +020075// rewritesEqual compares two slices of Rewrite structs for equality
76func rewritesEqual(a, b []Rewrite) bool {
77 if len(a) != len(b) {
78 return false
79 }
80 for i := range a {
81 if a[i].Editor != b[i].Editor ||
82 a[i].Operation != b[i].Operation ||
83 a[i].Scope != b[i].Scope ||
84 a[i].Src != b[i].Src ||
85 a[i].Comment != b[i].Comment ||
86 !reflect.DeepEqual(a[i].Original, b[i].Original) {
87 return false
88 }
89 }
90 return true
91}
92
Akron0d9117c2025-05-27 15:20:21 +020093// IsTermNode checks if a node is a Term node
94func IsTermNode(node Node) bool {
95 _, ok := node.(*Term)
96 return ok
97}