Support catchall nodes
diff --git a/pkg/matcher/matcher_test.go b/pkg/matcher/matcher_test.go
index d058a7c..adc8784 100644
--- a/pkg/matcher/matcher_test.go
+++ b/pkg/matcher/matcher_test.go
@@ -1,6 +1,10 @@
package matcher
+// matcher is a function that takes a pattern and a node and returns true if the node matches the pattern.
+// It is used to match a pattern against a node in the AST.
+
import (
+ "encoding/json"
"testing"
"github.com/KorAP/KoralPipe-TermMapper2/pkg/ast"
@@ -76,7 +80,7 @@
expected: false,
},
{
- name: "Wrong node type",
+ name: "Nested node",
input: &ast.Token{
Wrap: &ast.Term{
Foundry: "opennlp",
@@ -85,7 +89,7 @@
Match: ast.MatchEqual,
},
},
- expected: false,
+ expected: true,
},
}
@@ -502,3 +506,176 @@
assert.True(t, m.Match(input1), "Should match with original order")
assert.True(t, m.Match(input2), "Should match with reversed order")
}
+
+func TestMatchWithUnknownNodes(t *testing.T) {
+ // Create a pattern that looks for a term with DET inside any structure
+ pattern := ast.Pattern{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ }
+
+ replacement := ast.Replacement{
+ Root: &ast.Term{
+ Foundry: "opennlp",
+ Key: "COMBINED_DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ }
+
+ m := NewMatcher(pattern, replacement)
+
+ tests := []struct {
+ name string
+ input ast.Node
+ expected bool
+ }{
+ {
+ name: "Match term inside unknown node with wrap",
+ input: &ast.CatchallNode{
+ NodeType: "koral:custom",
+ RawContent: json.RawMessage(`{
+ "@type": "koral:custom",
+ "customField": "value"
+ }`),
+ Wrap: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "Match term inside unknown node's operands",
+ input: &ast.CatchallNode{
+ NodeType: "koral:custom",
+ RawContent: json.RawMessage(`{
+ "@type": "koral:custom",
+ "customField": "value"
+ }`),
+ Operands: []ast.Node{
+ &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "No match in unknown node with different term",
+ input: &ast.CatchallNode{
+ NodeType: "koral:custom",
+ RawContent: json.RawMessage(`{
+ "@type": "koral:custom",
+ "customField": "value"
+ }`),
+ Wrap: &ast.Term{
+ Foundry: "opennlp",
+ Key: "NOUN",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ expected: false,
+ },
+ {
+ name: "Match in deeply nested unknown nodes",
+ input: &ast.CatchallNode{
+ NodeType: "koral:outer",
+ RawContent: json.RawMessage(`{
+ "@type": "koral:outer",
+ "outerField": "value"
+ }`),
+ Wrap: &ast.CatchallNode{
+ NodeType: "koral:inner",
+ RawContent: json.RawMessage(`{
+ "@type": "koral:inner",
+ "innerField": "value"
+ }`),
+ Wrap: &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "Match in mixed known and unknown nodes",
+ input: &ast.Token{
+ Wrap: &ast.CatchallNode{
+ NodeType: "koral:custom",
+ RawContent: json.RawMessage(`{
+ "@type": "koral:custom",
+ "customField": "value"
+ }`),
+ Operands: []ast.Node{
+ &ast.TermGroup{
+ Operands: []ast.Node{
+ &ast.Term{
+ Foundry: "opennlp",
+ Key: "DET",
+ Layer: "p",
+ Match: ast.MatchEqual,
+ },
+ },
+ Relation: ast.AndRelation,
+ },
+ },
+ },
+ },
+ expected: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := m.Match(tt.input)
+ assert.Equal(t, tt.expected, result)
+
+ if tt.expected {
+ // Test replacement when there's a match
+ replaced := m.Replace(tt.input)
+ // Verify the replacement happened somewhere in the structure
+ containsReplacement := false
+ var checkNode func(ast.Node)
+ checkNode = func(node ast.Node) {
+ switch n := node.(type) {
+ case *ast.Term:
+ if n.Key == "COMBINED_DET" {
+ containsReplacement = true
+ }
+ case *ast.Token:
+ if n.Wrap != nil {
+ checkNode(n.Wrap)
+ }
+ case *ast.TermGroup:
+ for _, op := range n.Operands {
+ checkNode(op)
+ }
+ case *ast.CatchallNode:
+ if n.Wrap != nil {
+ checkNode(n.Wrap)
+ }
+ for _, op := range n.Operands {
+ checkNode(op)
+ }
+ }
+ }
+ checkNode(replaced)
+ assert.True(t, containsReplacement, "Replacement should be found in the result")
+ }
+ })
+ }
+}