Remove pkg subfolder
diff --git a/matcher/matcher.go b/matcher/matcher.go
new file mode 100644
index 0000000..7542bdf
--- /dev/null
+++ b/matcher/matcher.go
@@ -0,0 +1,443 @@
+package matcher
+
+import (
+	"fmt"
+
+	"github.com/KorAP/KoralPipe-TermMapper2/ast"
+)
+
+// Matcher handles pattern matching and replacement in the AST
+type Matcher struct {
+	pattern     ast.Pattern
+	replacement ast.Replacement
+}
+
+// validateNode checks if a node is valid for pattern/replacement ASTs
+func validateNode(node ast.Node) error {
+	if node == nil {
+		return fmt.Errorf("nil node")
+	}
+
+	switch n := node.(type) {
+	case *ast.Token:
+		if n.Wrap != nil {
+			return validateNode(n.Wrap)
+		}
+		return nil
+	case *ast.Term:
+		return nil
+	case *ast.TermGroup:
+		if len(n.Operands) == 0 {
+			return fmt.Errorf("empty term group")
+		}
+		for _, op := range n.Operands {
+			if err := validateNode(op); err != nil {
+				return fmt.Errorf("invalid operand: %v", err)
+			}
+		}
+		return nil
+	case *ast.CatchallNode:
+		return fmt.Errorf("catchall nodes are not allowed in pattern/replacement ASTs")
+	default:
+		return fmt.Errorf("unknown node type: %T", node)
+	}
+}
+
+// NewMatcher creates a new Matcher with the given pattern and replacement
+func NewMatcher(pattern ast.Pattern, replacement ast.Replacement) (*Matcher, error) {
+	if err := validateNode(pattern.Root); err != nil {
+		return nil, fmt.Errorf("invalid pattern: %v", err)
+	}
+	if err := validateNode(replacement.Root); err != nil {
+		return nil, fmt.Errorf("invalid replacement: %v", err)
+	}
+	return &Matcher{
+		pattern:     pattern,
+		replacement: replacement,
+	}, nil
+}
+
+// Match checks if the given node matches the pattern
+func (m *Matcher) Match(node ast.Node) bool {
+	return m.matchNode(node, m.pattern.Root)
+}
+
+// Replace replaces all occurrences of the pattern in the given node with the replacement
+func (m *Matcher) Replace(node ast.Node) ast.Node {
+	// First step: Create complete structure with replacements
+	replaced := m.replaceNode(node)
+	// Second step: Simplify the structure
+	simplified := m.simplifyNode(replaced)
+	// If the input was a Token, ensure the output is also a Token
+	if _, isToken := node.(*ast.Token); isToken {
+		if _, isToken := simplified.(*ast.Token); !isToken {
+			return &ast.Token{Wrap: simplified}
+		}
+	}
+	return simplified
+}
+
+// replaceNode creates a complete structure with replacements
+func (m *Matcher) replaceNode(node ast.Node) ast.Node {
+	if node == nil {
+		return nil
+	}
+
+	// First handle Token nodes specially to preserve their structure
+	if token, ok := node.(*ast.Token); ok {
+		if token.Wrap == nil {
+			return token
+		}
+		// Process the wrapped node
+		wrap := m.replaceNode(token.Wrap)
+		return &ast.Token{Wrap: wrap}
+	}
+
+	// If this node matches the pattern
+	if m.Match(node) {
+		// For TermGroups that contain a matching Term, preserve unmatched operands
+		if tg, ok := node.(*ast.TermGroup); ok {
+			// Check if any operand matches the pattern exactly
+			hasExactMatch := false
+			for _, op := range tg.Operands {
+				if m.matchNode(op, m.pattern.Root) {
+					hasExactMatch = true
+					break
+				}
+			}
+
+			// If we have an exact match, replace matching operands
+			if hasExactMatch {
+				hasMatch := false
+				newOperands := make([]ast.Node, 0, len(tg.Operands))
+				for _, op := range tg.Operands {
+					if m.matchNode(op, m.pattern.Root) {
+						if !hasMatch {
+							newOperands = append(newOperands, m.cloneNode(m.replacement.Root))
+							hasMatch = true
+						} else {
+							newOperands = append(newOperands, m.replaceNode(op))
+						}
+					} else {
+						newOperands = append(newOperands, m.replaceNode(op))
+					}
+				}
+				return &ast.TermGroup{
+					Operands: newOperands,
+					Relation: tg.Relation,
+				}
+			}
+			// Otherwise, replace the entire TermGroup
+			return m.cloneNode(m.replacement.Root)
+		}
+		// For other nodes, return the replacement
+		return m.cloneNode(m.replacement.Root)
+	}
+
+	// Otherwise recursively process children
+	switch n := node.(type) {
+	case *ast.TermGroup:
+		// Check if any operand matches the pattern exactly
+		hasExactMatch := false
+		for _, op := range n.Operands {
+			if m.matchNode(op, m.pattern.Root) {
+				hasExactMatch = true
+				break
+			}
+		}
+
+		// If we have an exact match, replace matching operands
+		if hasExactMatch {
+			hasMatch := false
+			newOperands := make([]ast.Node, 0, len(n.Operands))
+			for _, op := range n.Operands {
+				if m.matchNode(op, m.pattern.Root) {
+					if !hasMatch {
+						newOperands = append(newOperands, m.cloneNode(m.replacement.Root))
+						hasMatch = true
+					} else {
+						newOperands = append(newOperands, m.replaceNode(op))
+					}
+				} else {
+					newOperands = append(newOperands, m.replaceNode(op))
+				}
+			}
+			return &ast.TermGroup{
+				Operands: newOperands,
+				Relation: n.Relation,
+			}
+		}
+		// Otherwise, recursively process operands
+		newOperands := make([]ast.Node, len(n.Operands))
+		for i, op := range n.Operands {
+			newOperands[i] = m.replaceNode(op)
+		}
+		return &ast.TermGroup{
+			Operands: newOperands,
+			Relation: n.Relation,
+		}
+
+	case *ast.CatchallNode:
+		newNode := &ast.CatchallNode{
+			NodeType:   n.NodeType,
+			RawContent: n.RawContent,
+		}
+		if n.Wrap != nil {
+			newNode.Wrap = m.replaceNode(n.Wrap)
+		}
+		if len(n.Operands) > 0 {
+			newNode.Operands = make([]ast.Node, len(n.Operands))
+			for i, op := range n.Operands {
+				newNode.Operands[i] = m.replaceNode(op)
+			}
+		}
+		return newNode
+
+	default:
+		return node
+	}
+}
+
+// simplifyNode removes unnecessary wrappers and empty nodes
+func (m *Matcher) simplifyNode(node ast.Node) ast.Node {
+	if node == nil {
+		return nil
+	}
+
+	switch n := node.(type) {
+	case *ast.Token:
+		if n.Wrap == nil {
+			return nil
+		}
+		simplified := m.simplifyNode(n.Wrap)
+		if simplified == nil {
+			return nil
+		}
+		return &ast.Token{Wrap: simplified}
+
+	case *ast.TermGroup:
+		// First simplify all operands
+		simplified := make([]ast.Node, 0, len(n.Operands))
+		for _, op := range n.Operands {
+			if s := m.simplifyNode(op); s != nil {
+				simplified = append(simplified, s)
+			}
+		}
+
+		// Handle special cases
+		if len(simplified) == 0 {
+			return nil
+		}
+		if len(simplified) == 1 {
+			// If we have a single operand, return it directly
+			// But only if we're not inside a Token
+			if _, isToken := node.(*ast.Token); !isToken {
+				return simplified[0]
+			}
+		}
+
+		return &ast.TermGroup{
+			Operands: simplified,
+			Relation: n.Relation,
+		}
+
+	case *ast.CatchallNode:
+		newNode := &ast.CatchallNode{
+			NodeType:   n.NodeType,
+			RawContent: n.RawContent,
+		}
+		if n.Wrap != nil {
+			newNode.Wrap = m.simplifyNode(n.Wrap)
+		}
+		if len(n.Operands) > 0 {
+			simplified := make([]ast.Node, 0, len(n.Operands))
+			for _, op := range n.Operands {
+				if s := m.simplifyNode(op); s != nil {
+					simplified = append(simplified, s)
+				}
+			}
+			if len(simplified) > 0 {
+				newNode.Operands = simplified
+			}
+		}
+		return newNode
+
+	default:
+		return node
+	}
+}
+
+// matchNode recursively checks if two nodes match
+func (m *Matcher) matchNode(node, pattern ast.Node) bool {
+	if pattern == nil {
+		return true
+	}
+	if node == nil {
+		return false
+	}
+
+	// Handle pattern being a Token
+	if pToken, ok := pattern.(*ast.Token); ok {
+		if nToken, ok := node.(*ast.Token); ok {
+			return m.matchNode(nToken.Wrap, pToken.Wrap)
+		}
+		return false
+	}
+
+	// Handle pattern being a Term
+	if pTerm, ok := pattern.(*ast.Term); ok {
+		// Direct term to term matching
+		if t, ok := node.(*ast.Term); ok {
+			return t.Foundry == pTerm.Foundry &&
+				t.Key == pTerm.Key &&
+				t.Layer == pTerm.Layer &&
+				t.Match == pTerm.Match &&
+				(pTerm.Value == "" || t.Value == pTerm.Value)
+		}
+		// If node is a Token, check its wrap
+		if tkn, ok := node.(*ast.Token); ok {
+			if tkn.Wrap == nil {
+				return false
+			}
+			return m.matchNode(tkn.Wrap, pattern)
+		}
+		// If node is a TermGroup, check its operands
+		if tg, ok := node.(*ast.TermGroup); ok {
+			for _, op := range tg.Operands {
+				if m.matchNode(op, pattern) {
+					return true
+				}
+			}
+			return false
+		}
+		// If node is a CatchallNode, check its wrap and operands
+		if c, ok := node.(*ast.CatchallNode); ok {
+			if c.Wrap != nil && m.matchNode(c.Wrap, pattern) {
+				return true
+			}
+			for _, op := range c.Operands {
+				if m.matchNode(op, pattern) {
+					return true
+				}
+			}
+			return false
+		}
+		return false
+	}
+
+	// Handle pattern being a TermGroup
+	if pGroup, ok := pattern.(*ast.TermGroup); ok {
+		// For OR relations, check if any operand matches the node
+		if pGroup.Relation == ast.OrRelation {
+			for _, pOp := range pGroup.Operands {
+				if m.matchNode(node, pOp) {
+					return true
+				}
+			}
+			return false
+		}
+
+		// For AND relations, node must be a TermGroup with matching relation
+		if tg, ok := node.(*ast.TermGroup); ok {
+			if tg.Relation != pGroup.Relation {
+				return false
+			}
+			// Check that all pattern operands match in any order
+			if len(tg.Operands) < len(pGroup.Operands) {
+				return false
+			}
+			matched := make([]bool, len(tg.Operands))
+			for _, pOp := range pGroup.Operands {
+				found := false
+				for j, tOp := range tg.Operands {
+					if !matched[j] && m.matchNode(tOp, pOp) {
+						matched[j] = true
+						found = true
+						break
+					}
+				}
+				if !found {
+					return false
+				}
+			}
+			return true
+		}
+
+		// If node is a Token, check its wrap
+		if tkn, ok := node.(*ast.Token); ok {
+			if tkn.Wrap == nil {
+				return false
+			}
+			return m.matchNode(tkn.Wrap, pattern)
+		}
+
+		// If node is a CatchallNode, check its wrap and operands
+		if c, ok := node.(*ast.CatchallNode); ok {
+			if c.Wrap != nil && m.matchNode(c.Wrap, pattern) {
+				return true
+			}
+			for _, op := range c.Operands {
+				if m.matchNode(op, pattern) {
+					return true
+				}
+			}
+			return false
+		}
+
+		return false
+	}
+
+	return false
+}
+
+// cloneNode creates a deep copy of a node
+func (m *Matcher) cloneNode(node ast.Node) ast.Node {
+	if node == nil {
+		return nil
+	}
+
+	switch n := node.(type) {
+	case *ast.Token:
+		return &ast.Token{
+			Wrap: m.cloneNode(n.Wrap),
+		}
+
+	case *ast.TermGroup:
+		operands := make([]ast.Node, len(n.Operands))
+		for i, op := range n.Operands {
+			operands[i] = m.cloneNode(op)
+		}
+		return &ast.TermGroup{
+			Operands: operands,
+			Relation: n.Relation,
+		}
+
+	case *ast.Term:
+		return &ast.Term{
+			Foundry: n.Foundry,
+			Key:     n.Key,
+			Layer:   n.Layer,
+			Match:   n.Match,
+			Value:   n.Value,
+		}
+
+	case *ast.CatchallNode:
+		newNode := &ast.CatchallNode{
+			NodeType:   n.NodeType,
+			RawContent: n.RawContent,
+		}
+		if n.Wrap != nil {
+			newNode.Wrap = m.cloneNode(n.Wrap)
+		}
+		if len(n.Operands) > 0 {
+			newNode.Operands = make([]ast.Node, len(n.Operands))
+			for i, op := range n.Operands {
+				newNode.Operands[i] = m.cloneNode(op)
+			}
+		}
+		return newNode
+
+	default:
+		return nil
+	}
+}