Simplify parser

Change-Id: I5ae560d7c984cf9899b908adc2dd645714114202
diff --git a/parser/grammar_parser.go b/parser/grammar_parser.go
index 24cb15e..4862cea 100644
--- a/parser/grammar_parser.go
+++ b/parser/grammar_parser.go
@@ -141,75 +141,18 @@
 	}, nil
 }
 
-// Parse parses a grammar string into an AST node (for backward compatibility)
-func (p *GrammarParser) Parse(input string) (ast.Node, error) {
-	// Remove extra spaces around operators to help the parser
-	input = strings.ReplaceAll(input, " & ", "&")
-	input = strings.ReplaceAll(input, " | ", "|")
-
-	// Add spaces around parentheses that are not escaped
-	// We need to be careful not to break escape sequences like \(
-	result := make([]rune, 0, len(input)*2)
-	runes := []rune(input)
-	for i, r := range runes {
-		if (r == '(' || r == ')') && (i == 0 || runes[i-1] != '\\') {
-			// Only add spaces if the parenthesis is not escaped and not part of an identifier
-			// Check if this parenthesis is inside brackets (part of an identifier)
-			insideBrackets := false
-			bracketDepth := 0
-			for j := 0; j < i; j++ {
-				if runes[j] == '[' {
-					bracketDepth++
-				} else if runes[j] == ']' {
-					bracketDepth--
-				}
-			}
-			insideBrackets = bracketDepth > 0
-
-			if !insideBrackets {
-				result = append(result, ' ', r, ' ')
-			} else {
-				result = append(result, r)
-			}
-		} else {
-			result = append(result, r)
-		}
-	}
-	input = string(result)
-
-	// Remove any extra spaces
-	input = strings.TrimSpace(input)
-
-	grammar, err := p.tokenParser.ParseString("", input)
-	if err != nil {
-		return nil, fmt.Errorf("failed to parse grammar: %w", err)
-	}
-
-	if grammar.Token == nil {
-		return nil, fmt.Errorf("expected token expression, got mapping rule")
-	}
-
-	wrap, err := p.parseExpr(grammar.Token.Expr)
-	if err != nil {
-		return nil, err
-	}
-	return &ast.Token{Wrap: wrap}, nil
-}
-
-// ParseMapping parses a mapping rule string into a MappingResult
-func (p *GrammarParser) ParseMapping(input string) (*MappingResult, error) {
+// preprocessInput normalizes the input string by handling operators and parentheses
+func (p *GrammarParser) preprocessInput(input string) string {
 	// Remove extra spaces around operators to help the parser
 	input = strings.ReplaceAll(input, " & ", "&")
 	input = strings.ReplaceAll(input, " | ", "|")
 	input = strings.ReplaceAll(input, " <> ", "<>")
 
 	// Add spaces around parentheses that are not escaped
-	// We need to be careful not to break escape sequences like \(
 	result := make([]rune, 0, len(input)*2)
 	runes := []rune(input)
 	for i, r := range runes {
 		if (r == '(' || r == ')') && (i == 0 || runes[i-1] != '\\') {
-			// Only add spaces if the parenthesis is not escaped and not part of an identifier
 			// Check if this parenthesis is inside brackets (part of an identifier)
 			insideBrackets := false
 			bracketDepth := 0
@@ -231,10 +174,12 @@
 			result = append(result, r)
 		}
 	}
-	input = string(result)
+	return strings.TrimSpace(string(result))
+}
 
-	// Remove any extra spaces
-	input = strings.TrimSpace(input)
+// ParseMapping parses a mapping rule string into a MappingResult
+func (p *GrammarParser) ParseMapping(input string) (*MappingResult, error) {
+	input = p.preprocessInput(input)
 
 	grammar, err := p.mappingParser.ParseString("", input)
 	if err != nil {
diff --git a/parser/grammar_parser_test.go b/parser/grammar_parser_test.go
index 5c0cc56..07f61e9 100644
--- a/parser/grammar_parser_test.go
+++ b/parser/grammar_parser_test.go
@@ -126,151 +126,8 @@
 	}
 }
 
-func TestGrammarParser(t *testing.T) {
-	tests := []struct {
-		name           string
-		input          string
-		defaultFoundry string
-		defaultLayer   string
-		expected       ast.Node
-		expectError    bool
-	}{
-		{
-			name:           "Simple term with foundry and layer",
-			input:          "[opennlp/p=PIDAT]",
-			defaultFoundry: "opennlp",
-			defaultLayer:   "p",
-			expected: &ast.Token{
-				Wrap: &ast.Term{
-					Foundry: "opennlp",
-					Key:     "PIDAT",
-					Layer:   "p",
-					Match:   ast.MatchEqual,
-				},
-			},
-		},
-		{
-			name:           "Term group with and relation",
-			input:          "[opennlp/p=PIDAT & opennlp/p=AdjType:Pdt]",
-			defaultFoundry: "opennlp",
-			defaultLayer:   "p",
-			expected: &ast.Token{
-				Wrap: &ast.TermGroup{
-					Operands: []ast.Node{
-						&ast.Term{
-							Foundry: "opennlp",
-							Key:     "PIDAT",
-							Layer:   "p",
-							Match:   ast.MatchEqual,
-						},
-						&ast.Term{
-							Foundry: "opennlp",
-							Key:     "AdjType",
-							Layer:   "p",
-							Match:   ast.MatchEqual,
-							Value:   "Pdt",
-						},
-					},
-					Relation: ast.AndRelation,
-				},
-			},
-		},
-		{
-			name:           "Term group with or relation",
-			input:          "[opennlp/p=PronType:Ind | opennlp/p=PronType:Neg]",
-			defaultFoundry: "opennlp",
-			defaultLayer:   "p",
-			expected: &ast.Token{
-				Wrap: &ast.TermGroup{
-					Operands: []ast.Node{
-						&ast.Term{
-							Foundry: "opennlp",
-							Key:     "PronType",
-							Layer:   "p",
-							Match:   ast.MatchEqual,
-							Value:   "Ind",
-						},
-						&ast.Term{
-							Foundry: "opennlp",
-							Key:     "PronType",
-							Layer:   "p",
-							Match:   ast.MatchEqual,
-							Value:   "Neg",
-						},
-					},
-					Relation: ast.OrRelation,
-				},
-			},
-		},
-		{
-			name:           "Complex term group",
-			input:          "[opennlp/p=PIDAT & (opennlp/p=PronType:Ind | opennlp/p=PronType:Neg)]",
-			defaultFoundry: "opennlp",
-			defaultLayer:   "p",
-			expected: &ast.Token{
-				Wrap: &ast.TermGroup{
-					Operands: []ast.Node{
-						&ast.Term{
-							Foundry: "opennlp",
-							Key:     "PIDAT",
-							Layer:   "p",
-							Match:   ast.MatchEqual,
-						},
-						&ast.TermGroup{
-							Operands: []ast.Node{
-								&ast.Term{
-									Foundry: "opennlp",
-									Key:     "PronType",
-									Layer:   "p",
-									Match:   ast.MatchEqual,
-									Value:   "Ind",
-								},
-								&ast.Term{
-									Foundry: "opennlp",
-									Key:     "PronType",
-									Layer:   "p",
-									Match:   ast.MatchEqual,
-									Value:   "Neg",
-								},
-							},
-							Relation: ast.OrRelation,
-						},
-					},
-					Relation: ast.AndRelation,
-				},
-			},
-		},
-		{
-			name:           "Wildcard pattern",
-			input:          "[opennlp/*=PIDAT]",
-			defaultFoundry: "opennlp",
-			defaultLayer:   "p",
-			expected: &ast.Token{
-				Wrap: &ast.Term{
-					Foundry: "opennlp",
-					Key:     "PIDAT",
-					Layer:   "p",
-					Match:   ast.MatchEqual,
-				},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			parser, err := NewGrammarParser(tt.defaultFoundry, tt.defaultLayer)
-			require.NoError(t, err)
-
-			result, err := parser.Parse(tt.input)
-			if tt.expectError {
-				assert.Error(t, err)
-				return
-			}
-			require.NoError(t, err)
-			assert.Equal(t, tt.expected, result)
-		})
-	}
-}
+// TestGrammarParser was removed as the Parse method is no longer supported
+// The functionality is now only available through ParseMapping method
 
 func TestMappingRules(t *testing.T) {
 	tests := []struct {
@@ -367,6 +224,156 @@
 				},
 			},
 		},
+		// Additional tests to cover functionality from removed TestGrammarParser
+		{
+			name:  "Simple term with foundry and layer",
+			input: "[opennlp/p=PIDAT] <> [PIDAT]",
+			expected: &MappingResult{
+				Upper: &ast.Token{
+					Wrap: &ast.Term{
+						Foundry: "opennlp",
+						Layer:   "p",
+						Key:     "PIDAT",
+						Match:   ast.MatchEqual,
+					},
+				},
+				Lower: &ast.Token{
+					Wrap: &ast.Term{
+						Key:   "PIDAT",
+						Match: ast.MatchEqual,
+					},
+				},
+			},
+		},
+		{
+			name:  "Term group with and relation",
+			input: "[opennlp/p=PIDAT & opennlp/p=AdjType:Pdt] <> [PIDAT]",
+			expected: &MappingResult{
+				Upper: &ast.Token{
+					Wrap: &ast.TermGroup{
+						Operands: []ast.Node{
+							&ast.Term{
+								Foundry: "opennlp",
+								Layer:   "p",
+								Key:     "PIDAT",
+								Match:   ast.MatchEqual,
+							},
+							&ast.Term{
+								Foundry: "opennlp",
+								Layer:   "p",
+								Key:     "AdjType",
+								Value:   "Pdt",
+								Match:   ast.MatchEqual,
+							},
+						},
+						Relation: ast.AndRelation,
+					},
+				},
+				Lower: &ast.Token{
+					Wrap: &ast.Term{
+						Key:   "PIDAT",
+						Match: ast.MatchEqual,
+					},
+				},
+			},
+		},
+		{
+			name:  "Term group with or relation",
+			input: "[opennlp/p=PronType:Ind | opennlp/p=PronType:Neg] <> [PRON]",
+			expected: &MappingResult{
+				Upper: &ast.Token{
+					Wrap: &ast.TermGroup{
+						Operands: []ast.Node{
+							&ast.Term{
+								Foundry: "opennlp",
+								Layer:   "p",
+								Key:     "PronType",
+								Value:   "Ind",
+								Match:   ast.MatchEqual,
+							},
+							&ast.Term{
+								Foundry: "opennlp",
+								Layer:   "p",
+								Key:     "PronType",
+								Value:   "Neg",
+								Match:   ast.MatchEqual,
+							},
+						},
+						Relation: ast.OrRelation,
+					},
+				},
+				Lower: &ast.Token{
+					Wrap: &ast.Term{
+						Key:   "PRON",
+						Match: ast.MatchEqual,
+					},
+				},
+			},
+		},
+		{
+			name:  "Complex term group with nested parentheses",
+			input: "[opennlp/p=PIDAT & (opennlp/p=PronType:Ind | opennlp/p=PronType:Neg)] <> [COMPLEX]",
+			expected: &MappingResult{
+				Upper: &ast.Token{
+					Wrap: &ast.TermGroup{
+						Operands: []ast.Node{
+							&ast.Term{
+								Foundry: "opennlp",
+								Layer:   "p",
+								Key:     "PIDAT",
+								Match:   ast.MatchEqual,
+							},
+							&ast.TermGroup{
+								Operands: []ast.Node{
+									&ast.Term{
+										Foundry: "opennlp",
+										Layer:   "p",
+										Key:     "PronType",
+										Value:   "Ind",
+										Match:   ast.MatchEqual,
+									},
+									&ast.Term{
+										Foundry: "opennlp",
+										Layer:   "p",
+										Key:     "PronType",
+										Value:   "Neg",
+										Match:   ast.MatchEqual,
+									},
+								},
+								Relation: ast.OrRelation,
+							},
+						},
+						Relation: ast.AndRelation,
+					},
+				},
+				Lower: &ast.Token{
+					Wrap: &ast.Term{
+						Key:   "COMPLEX",
+						Match: ast.MatchEqual,
+					},
+				},
+			},
+		},
+		{
+			name:  "Wildcard pattern",
+			input: "[opennlp/*=PIDAT] <> [PIDAT]",
+			expected: &MappingResult{
+				Upper: &ast.Token{
+					Wrap: &ast.Term{
+						Foundry: "opennlp",
+						Layer:   "",
+						Key:     "PIDAT",
+						Match:   ast.MatchEqual,
+					},
+				},
+				Lower: &ast.Token{
+					Wrap: &ast.Term{
+						Key:   "PIDAT",
+						Match: ast.MatchEqual,
+					},
+				},
+			},
+		},
 		{
 			name:    "Invalid mapping syntax",
 			input:   "[PAV] -> [ADV]",
diff --git a/parser/parser.go b/parser/parser.go
index 0dc2b03..e122edc 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -242,42 +242,22 @@
 				return nil, fmt.Errorf("failed to parse 'wrap' field in unknown node type '%s': %w", raw.Type, err)
 			}
 
-			// Check if the wrapped node is a known type
-			if wrapRaw.Type == "koral:term" || wrapRaw.Type == "koral:token" || wrapRaw.Type == "koral:termGroup" {
-				wrap, err := parseNode(wrapRaw)
-				if err != nil {
-					return nil, fmt.Errorf("error parsing wrapped node in unknown node type '%s': %w", raw.Type, err)
-				}
-				catchall.Wrap = wrap
-			} else {
-				// For unknown types, recursively parse
-				wrap, err := parseNode(wrapRaw)
-				if err != nil {
-					return nil, fmt.Errorf("error parsing wrapped node in unknown node type '%s': %w", raw.Type, err)
-				}
-				catchall.Wrap = wrap
+			wrap, err := parseNode(wrapRaw)
+			if err != nil {
+				return nil, fmt.Errorf("error parsing wrapped node in unknown node type '%s': %w", raw.Type, err)
 			}
+			catchall.Wrap = wrap
 		}
 
 		// Parse operands if present
 		if len(raw.Operands) > 0 {
 			operands := make([]ast.Node, len(raw.Operands))
 			for i, op := range raw.Operands {
-				// Check if the operand is a known type
-				if op.Type == "koral:term" || op.Type == "koral:token" || op.Type == "koral:termGroup" {
-					node, err := parseNode(op)
-					if err != nil {
-						return nil, fmt.Errorf("error parsing operand %d in unknown node type '%s': %w", i+1, raw.Type, err)
-					}
-					operands[i] = node
-				} else {
-					// For unknown types, recursively parse
-					node, err := parseNode(op)
-					if err != nil {
-						return nil, fmt.Errorf("error parsing operand %d in unknown node type '%s': %w", i+1, raw.Type, err)
-					}
-					operands[i] = node
+				node, err := parseNode(op)
+				if err != nil {
+					return nil, fmt.Errorf("error parsing operand %d in unknown node type '%s': %w", i+1, raw.Type, err)
 				}
+				operands[i] = node
 			}
 			catchall.Operands = operands
 		}
diff --git a/parser/title_parser.go b/parser/title_parser.go
index 839ff5a..87bd809 100644
--- a/parser/title_parser.go
+++ b/parser/title_parser.go
@@ -30,9 +30,9 @@
 	}
 }
 
-// ParseTitleAttribute parses a single title attribute string
+// parseTitleAttribute parses a single title attribute string
 // Expects format: "foundry/layer:key" or "foundry/layer:key[:=]value"
-func (p *TitleAttributeParser) ParseTitleAttribute(title string) (*TitleAttribute, error) {
+func (p *TitleAttributeParser) parseTitleAttribute(title string) (*TitleAttribute, error) {
 	if title == "" {
 		return nil, fmt.Errorf("empty title attribute")
 	}
@@ -63,7 +63,7 @@
 	terms := make([]ast.Node, 0) // Initialize as empty slice instead of nil
 
 	for _, title := range titles {
-		attr, err := p.ParseTitleAttribute(title)
+		attr, err := p.parseTitleAttribute(title)
 		if err != nil {
 			return nil, fmt.Errorf("failed to parse title '%s': %w", title, err)
 		}
@@ -81,22 +81,3 @@
 
 	return terms, nil
 }
-
-// ToAST converts a TitleAttribute to an AST Term node
-func (attr *TitleAttribute) ToAST() ast.Node {
-	return &ast.Term{
-		Foundry: attr.Foundry,
-		Layer:   attr.Layer,
-		Key:     attr.Key,
-		Value:   attr.Value,
-		Match:   ast.MatchEqual,
-	}
-}
-
-// String returns a string representation of the title attribute
-func (attr *TitleAttribute) String() string {
-	if attr.Value != "" {
-		return fmt.Sprintf("%s/%s:%s=%s", attr.Foundry, attr.Layer, attr.Key, attr.Value)
-	}
-	return fmt.Sprintf("%s/%s:%s", attr.Foundry, attr.Layer, attr.Key)
-}
diff --git a/parser/title_parser_test.go b/parser/title_parser_test.go
index 326625f..1a74905 100644
--- a/parser/title_parser_test.go
+++ b/parser/title_parser_test.go
@@ -8,126 +8,8 @@
 	"github.com/stretchr/testify/require"
 )
 
-func TestTitleAttributeParser_ParseTitleAttribute(t *testing.T) {
-	parser := NewTitleAttributeParser()
-
-	tests := []struct {
-		name     string
-		input    string
-		expected *TitleAttribute
-		wantErr  bool
-	}{
-		{
-			name:  "Parse simple title with key only",
-			input: "corenlp/p:ART",
-			expected: &TitleAttribute{
-				Foundry: "corenlp",
-				Layer:   "p",
-				Key:     "ART",
-				Value:   "",
-			},
-			wantErr: false,
-		},
-		{
-			name:  "Parse title with key and value",
-			input: "marmot/m:case:nom",
-			expected: &TitleAttribute{
-				Foundry: "marmot",
-				Layer:   "m",
-				Key:     "case",
-				Value:   "nom",
-			},
-			wantErr: false,
-		},
-		{
-			name:  "Parse title with colon separator for value",
-			input: "marmot/m:gender:masc",
-			expected: &TitleAttribute{
-				Foundry: "marmot",
-				Layer:   "m",
-				Key:     "gender",
-				Value:   "masc",
-			},
-			wantErr: false,
-		},
-		{
-			name:  "Parse title with equals separator for value",
-			input: "marmot/m:degree:pos",
-			expected: &TitleAttribute{
-				Foundry: "marmot",
-				Layer:   "m",
-				Key:     "degree",
-				Value:   "pos",
-			},
-			wantErr: false,
-		},
-		{
-			name:  "Parse title with lemma layer",
-			input: "tt/l:die",
-			expected: &TitleAttribute{
-				Foundry: "tt",
-				Layer:   "l",
-				Key:     "die",
-				Value:   "",
-			},
-			wantErr: false,
-		},
-		{
-			name:  "Parse title with special characters in value",
-			input: "tt/l:@card@",
-			expected: &TitleAttribute{
-				Foundry: "tt",
-				Layer:   "l",
-				Key:     "@card@",
-				Value:   "",
-			},
-			wantErr: false,
-		},
-		{
-			name:    "Empty title should fail",
-			input:   "",
-			wantErr: true,
-		},
-		{
-			name:    "Missing foundry separator should fail",
-			input:   "corenlp_p:ART",
-			wantErr: true,
-		},
-		{
-			name:    "Missing layer separator should fail",
-			input:   "corenlp/p_ART",
-			wantErr: true,
-		},
-		{
-			name:    "Only foundry should fail",
-			input:   "corenlp",
-			wantErr: true,
-		},
-		{
-			name:    "Only foundry and layer should fail",
-			input:   "corenlp/p",
-			wantErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result, err := parser.ParseTitleAttribute(tt.input)
-
-			if tt.wantErr {
-				assert.Error(t, err)
-				assert.Nil(t, result)
-			} else {
-				require.NoError(t, err)
-				require.NotNil(t, result)
-				assert.Equal(t, tt.expected.Foundry, result.Foundry)
-				assert.Equal(t, tt.expected.Layer, result.Layer)
-				assert.Equal(t, tt.expected.Key, result.Key)
-				assert.Equal(t, tt.expected.Value, result.Value)
-			}
-		})
-	}
-}
+// TestTitleAttributeParser_ParseTitleAttribute was removed as ParseTitleAttribute is no longer exported
+// The functionality is now only available through ParseTitleAttributesToTerms method
 
 func TestTitleAttributeParser_ParseTitleAttributesToTerms(t *testing.T) {
 	parser := NewTitleAttributeParser()
@@ -177,6 +59,164 @@
 			input:   []string{"corenlp/p:ART", "invalid_title", "tt/l:die"},
 			wantErr: true,
 		},
+		// Additional tests to cover functionality from removed TestTitleAttributeParser_ParseTitleAttribute
+		{
+			name:  "Parse simple title with key only",
+			input: []string{"corenlp/p:ART"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "corenlp",
+					Layer:   "p",
+					Key:     "ART",
+					Value:   "",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse title with key and value",
+			input: []string{"marmot/m:case:nom"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "marmot",
+					Layer:   "m",
+					Key:     "case",
+					Value:   "nom",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse title with colon separator for value",
+			input: []string{"marmot/m:gender:masc"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "marmot",
+					Layer:   "m",
+					Key:     "gender",
+					Value:   "masc",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse title with equals separator for value",
+			input: []string{"marmot/m:degree:pos"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "marmot",
+					Layer:   "m",
+					Key:     "degree",
+					Value:   "pos",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse title with lemma layer",
+			input: []string{"tt/l:die"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "tt",
+					Layer:   "l",
+					Key:     "die",
+					Value:   "",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse title with special characters in value",
+			input: []string{"tt/l:@card@"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "tt",
+					Layer:   "l",
+					Key:     "@card@",
+					Value:   "",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse complex key-value with colon",
+			input: []string{"opennlp/p:PronType:Ind"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "opennlp",
+					Layer:   "p",
+					Key:     "PronType",
+					Value:   "Ind",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse complex key-value with equals",
+			input: []string{"opennlp/p:AdjType:Pdt"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "opennlp",
+					Layer:   "p",
+					Key:     "AdjType",
+					Value:   "Pdt",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:  "Parse complex nested pattern",
+			input: []string{"stts/p:ADJA"},
+			expected: []ast.Node{
+				&ast.Term{
+					Foundry: "stts",
+					Layer:   "p",
+					Key:     "ADJA",
+					Value:   "",
+					Match:   ast.MatchEqual,
+				},
+			},
+			wantErr: false,
+		},
+		// Error cases
+		{
+			name:    "Empty title should fail",
+			input:   []string{""},
+			wantErr: true,
+		},
+		{
+			name:    "Missing foundry separator should fail",
+			input:   []string{"corenlp_p:ART"},
+			wantErr: true,
+		},
+		{
+			name:    "Missing layer separator should fail",
+			input:   []string{"corenlp/p_ART"},
+			wantErr: true,
+		},
+		{
+			name:    "Only foundry should fail",
+			input:   []string{"corenlp"},
+			wantErr: true,
+		},
+		{
+			name:    "Only foundry and layer should fail",
+			input:   []string{"corenlp/p"},
+			wantErr: true,
+		},
+		{
+			name:    "Missing key should fail",
+			input:   []string{"corenlp/p:"},
+			wantErr: true,
+		},
 	}
 
 	for _, tt := range tests {
@@ -204,128 +244,6 @@
 	}
 }
 
-func TestTitleAttribute_ToAST(t *testing.T) {
-	tests := []struct {
-		name     string
-		attr     *TitleAttribute
-		expected *ast.Term
-	}{
-		{
-			name: "Convert title attribute to AST term",
-			attr: &TitleAttribute{
-				Foundry: "corenlp",
-				Layer:   "p",
-				Key:     "ART",
-				Value:   "",
-			},
-			expected: &ast.Term{
-				Foundry: "corenlp",
-				Layer:   "p",
-				Key:     "ART",
-				Value:   "",
-				Match:   ast.MatchEqual,
-			},
-		},
-		{
-			name: "Convert title attribute with value to AST term",
-			attr: &TitleAttribute{
-				Foundry: "marmot",
-				Layer:   "m",
-				Key:     "case",
-				Value:   "nom",
-			},
-			expected: &ast.Term{
-				Foundry: "marmot",
-				Layer:   "m",
-				Key:     "case",
-				Value:   "nom",
-				Match:   ast.MatchEqual,
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := tt.attr.ToAST()
-
-			termResult := result.(*ast.Term)
-			assert.Equal(t, tt.expected.Foundry, termResult.Foundry)
-			assert.Equal(t, tt.expected.Layer, termResult.Layer)
-			assert.Equal(t, tt.expected.Key, termResult.Key)
-			assert.Equal(t, tt.expected.Value, termResult.Value)
-			assert.Equal(t, tt.expected.Match, termResult.Match)
-		})
-	}
-}
-
-func TestTitleAttribute_String(t *testing.T) {
-	tests := []struct {
-		name     string
-		attr     *TitleAttribute
-		expected string
-	}{
-		{
-			name: "String representation without value",
-			attr: &TitleAttribute{
-				Foundry: "corenlp",
-				Layer:   "p",
-				Key:     "ART",
-				Value:   "",
-			},
-			expected: "corenlp/p:ART",
-		},
-		{
-			name: "String representation with value",
-			attr: &TitleAttribute{
-				Foundry: "marmot",
-				Layer:   "m",
-				Key:     "case",
-				Value:   "nom",
-			},
-			expected: "marmot/m:case=nom",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := tt.attr.String()
-			assert.Equal(t, tt.expected, result)
-		})
-	}
-}
-
-func TestTitleAttributeParser_RealWorldExample(t *testing.T) {
-	parser := NewTitleAttributeParser()
-
-	// Example titles from the response test file
-	titles := []string{
-		"corenlp/p:ART",
-		"marmot/m:case=nom",
-		"marmot/m:gender=masc",
-		"marmot/m:number=sg",
-		"marmot/p:ART",
-		"opennlp/p:ART",
-		"tt/l:die",
-		"tt/p:ART",
-	}
-
-	// Parse each title attribute
-	for _, title := range titles {
-		attr, err := parser.ParseTitleAttribute(title)
-		require.NoError(t, err)
-		require.NotNil(t, attr)
-
-		// Verify the string representation matches
-		assert.Equal(t, title, attr.String())
-
-		// Verify conversion to AST works
-		astNode := attr.ToAST()
-		require.NotNil(t, astNode)
-
-		term := astNode.(*ast.Term)
-		assert.NotEmpty(t, term.Foundry)
-		assert.NotEmpty(t, term.Layer)
-		assert.NotEmpty(t, term.Key)
-		assert.Equal(t, ast.MatchEqual, term.Match)
-	}
-}
+// TestTitleAttribute_ToAST was removed as ToAST method is no longer available
+// TestTitleAttribute_String was removed as String method is no longer available
+// TestTitleAttributeParser_RealWorldExample was removed as it used the removed methods