Simplify ast implementation

Change-Id: I0e61cde0e8551103e362a14de880d77fc9315807
diff --git a/ast/ast.go b/ast/ast.go
index 0f1c858..6461a7b 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -356,6 +356,17 @@
 //   - (a & b & (c | d) & e) -> (a & b & e) (OR-relation removed)
 //   - (a | b) -> nil (completely optional)
 func RestrictToObligatory(node Node, foundry, layer string) Node {
+	return restrictToObligatoryWithOverrides(node, foundry, layer, false)
+}
+
+// RestrictToObligatoryWithPrecedence is like RestrictToObligatory but respects precedence rules
+// when applying foundry and layer overrides
+func RestrictToObligatoryWithPrecedence(node Node, foundry, layer string) Node {
+	return restrictToObligatoryWithOverrides(node, foundry, layer, true)
+}
+
+// restrictToObligatoryWithOverrides performs the restriction and applies overrides with optional precedence
+func restrictToObligatoryWithOverrides(node Node, foundry, layer string, withPrecedence bool) Node {
 	if node == nil {
 		return nil
 	}
@@ -366,26 +377,11 @@
 
 	// Then apply foundry and layer overrides to the smaller, restricted tree
 	if restricted != nil {
-		ApplyFoundryAndLayerOverrides(restricted, foundry, layer)
-	}
-
-	return restricted
-}
-
-// RestrictToObligatoryWithPrecedence is like RestrictToObligatory but respects precedence rules
-// when applying foundry and layer overrides
-func RestrictToObligatoryWithPrecedence(node Node, foundry, layer string) Node {
-	if node == nil {
-		return nil
-	}
-
-	// First, clone and restrict to obligatory operations
-	cloned := node.Clone()
-	restricted := restrictToObligatoryRecursive(cloned)
-
-	// Then apply foundry and layer overrides with precedence to the smaller, restricted tree
-	if restricted != nil {
-		ApplyFoundryAndLayerOverridesWithPrecedence(restricted, foundry, layer)
+		if withPrecedence {
+			ApplyFoundryAndLayerOverridesWithPrecedence(restricted, foundry, layer)
+		} else {
+			ApplyFoundryAndLayerOverrides(restricted, foundry, layer)
+		}
 	}
 
 	return restricted
@@ -420,7 +416,9 @@
 		if n.Relation == OrRelation {
 			// OR-relations are optional, so remove them
 			return nil
-		} else if n.Relation == AndRelation {
+		}
+
+		if n.Relation == AndRelation {
 			// AND-relations are obligatory, but we need to process operands
 			var obligatoryOperands []Node
 			for _, operand := range n.Operands {
diff --git a/ast/compare.go b/ast/compare.go
index 617e8f0..df4b28f 100644
--- a/ast/compare.go
+++ b/ast/compare.go
@@ -4,7 +4,7 @@
 	"reflect"
 )
 
-// NodesEqual compares two AST nodes for equality
+// NodesEqual compares two AST nodes for structural equality, ignoring rewrites metadata
 func NodesEqual(a, b Node) bool {
 	if a == nil || b == nil {
 		return a == b
@@ -21,8 +21,7 @@
 				n1.Key == n2.Key &&
 				n1.Layer == n2.Layer &&
 				n1.Match == n2.Match &&
-				n1.Value == n2.Value &&
-				rewritesEqual(n1.Rewrites, n2.Rewrites)
+				n1.Value == n2.Value
 		}
 	case *TermGroup:
 		if n2, ok := b.(*TermGroup); ok {
@@ -34,12 +33,11 @@
 					return false
 				}
 			}
-			return rewritesEqual(n1.Rewrites, n2.Rewrites)
+			return true
 		}
 	case *Token:
 		if n2, ok := b.(*Token); ok {
-			return NodesEqual(n1.Wrap, n2.Wrap) &&
-				rewritesEqual(n1.Rewrites, n2.Rewrites)
+			return NodesEqual(n1.Wrap, n2.Wrap)
 		}
 	case *CatchallNode:
 		if n2, ok := b.(*CatchallNode); ok {
@@ -72,24 +70,6 @@
 	return false
 }
 
-// rewritesEqual compares two slices of Rewrite structs for equality
-func rewritesEqual(a, b []Rewrite) bool {
-	if len(a) != len(b) {
-		return false
-	}
-	for i := range a {
-		if a[i].Editor != b[i].Editor ||
-			a[i].Operation != b[i].Operation ||
-			a[i].Scope != b[i].Scope ||
-			a[i].Src != b[i].Src ||
-			a[i].Comment != b[i].Comment ||
-			!reflect.DeepEqual(a[i].Original, b[i].Original) {
-			return false
-		}
-	}
-	return true
-}
-
 // IsTermNode checks if a node is a Term node
 func IsTermNode(node Node) bool {
 	_, ok := node.(*Term)