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
+ }
+}