Support wildcards for layers

Change-Id: I06db6e822ca60b355cc3e86b504dba1cf3c92845
diff --git a/parser/grammar_parser.go b/parser/grammar_parser.go
index 81ebe62..8ee167f 100644
--- a/parser/grammar_parser.go
+++ b/parser/grammar_parser.go
@@ -61,10 +61,11 @@
 
 // SimpleTerm represents any valid term form
 type SimpleTerm struct {
-	WithFoundryLayer *FoundryLayerTerm `parser:"@@"`
-	WithFoundryKey   *FoundryKeyTerm   `parser:"| @@"`
-	WithLayer        *LayerTerm        `parser:"| @@"`
-	SimpleKey        *KeyTerm          `parser:"| @@"`
+	WithFoundryLayer    *FoundryLayerTerm    `parser:"@@"`
+	WithFoundryWildcard *FoundryWildcardTerm `parser:"| @@"`
+	WithFoundryKey      *FoundryKeyTerm      `parser:"| @@"`
+	WithLayer           *LayerTerm           `parser:"| @@"`
+	SimpleKey           *KeyTerm             `parser:"| @@"`
 }
 
 // FoundryLayerTerm represents foundry/layer=key:value
@@ -75,30 +76,42 @@
 	Value   string `parser:"(':' @Ident)?"`
 }
 
+// FoundryWildcardTerm represents foundry/*=key (wildcard layer)
+type FoundryWildcardTerm struct {
+	Foundry string `parser:"@Ident '/' '*' '='"`
+	Key     string `parser:"@Ident"`
+}
+
 // FoundryKeyTerm represents foundry/key
 type FoundryKeyTerm struct {
 	Foundry string `parser:"@Ident '/'"`
 	Key     string `parser:"@Ident"`
 }
 
-// LayerTerm represents layer=key:value
+// LayerTerm represents layer=key:value (only when no foundry is present)
 type LayerTerm struct {
 	Layer string `parser:"@Ident '='"`
 	Key   string `parser:"@Ident"`
 	Value string `parser:"(':' @Ident)?"`
 }
 
-// KeyTerm represents key:value
+// KeyTerm represents key:value or key=value
 type KeyTerm struct {
 	Key   string `parser:"@Ident"`
-	Value string `parser:"(':' @Ident)?"`
+	Value string `parser:"((':' | '=') @Ident)?"`
+}
+
+// EscapedPunct represents an escaped punctuation character like \(
+type EscapedPunct struct {
+	Prefix string `parser:"@Ident"`
+	Punct  string `parser:"@Punct"`
 }
 
 // NewGrammarParser creates a new grammar parser with optional default foundry and layer
 func NewGrammarParser(defaultFoundry, defaultLayer string) (*GrammarParser, error) {
 	lex := lexer.MustSimple([]lexer.SimpleRule{
-		{Name: "Ident", Pattern: `(?:[a-zA-Z$]|\\.)(?:[a-zA-Z0-9_$]|\\.)*`},
-		{Name: "Punct", Pattern: `[\[\]()&\|=:/]|<>`},
+		{Name: "Ident", Pattern: `(?:[a-zA-Z$,.]|\\.)(?:[a-zA-Z0-9_$,.]|\\.)*`},
+		{Name: "Punct", Pattern: `[\[\]()&\|=:/\*]|<>`},
 		{Name: "Whitespace", Pattern: `\s+`},
 	})
 
@@ -140,8 +153,24 @@
 	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
-			result = append(result, ' ', r, ' ')
+			// 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)
 		}
@@ -180,8 +209,24 @@
 	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
-			result = append(result, ' ', r, ' ')
+			// 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)
 		}
@@ -340,13 +385,34 @@
 		layer = unescapeString(term.WithFoundryLayer.Layer)
 		key = unescapeString(term.WithFoundryLayer.Key)
 		value = unescapeString(term.WithFoundryLayer.Value)
+	case term.WithFoundryWildcard != nil:
+		foundry = unescapeString(term.WithFoundryWildcard.Foundry)
+		key = unescapeString(term.WithFoundryWildcard.Key)
 	case term.WithFoundryKey != nil:
 		foundry = unescapeString(term.WithFoundryKey.Foundry)
 		key = unescapeString(term.WithFoundryKey.Key)
 	case term.WithLayer != nil:
-		layer = unescapeString(term.WithLayer.Layer)
-		key = unescapeString(term.WithLayer.Key)
-		value = unescapeString(term.WithLayer.Value)
+		// Special case: if LayerTerm was parsed but the layer doesn't match the default layer,
+		// treat it as a key=value pattern instead
+		parsedLayer := unescapeString(term.WithLayer.Layer)
+		parsedKey := unescapeString(term.WithLayer.Key)
+		parsedValue := unescapeString(term.WithLayer.Value)
+
+		if p.defaultLayer != "" && parsedLayer == p.defaultLayer {
+			// This is a genuine layer=key pattern when the layer matches the default
+			layer = parsedLayer
+			key = parsedKey
+			value = parsedValue
+		} else if p.defaultLayer != "" && parsedLayer != p.defaultLayer {
+			// This should be treated as key=value pattern when there's a default layer but it doesn't match
+			key = parsedLayer
+			value = parsedKey
+		} else {
+			// No default layer context, treat as genuine layer=key pattern
+			layer = parsedLayer
+			key = parsedKey
+			value = parsedValue
+		}
 	case term.SimpleKey != nil:
 		key = unescapeString(term.SimpleKey.Key)
 		value = unescapeString(term.SimpleKey.Value)
diff --git a/parser/grammar_parser_test.go b/parser/grammar_parser_test.go
index 189ff68..5c0cc56 100644
--- a/parser/grammar_parser_test.go
+++ b/parser/grammar_parser_test.go
@@ -89,6 +89,18 @@
 				},
 			},
 		},
+		{
+			name:           "Foundry wildcard key",
+			input:          "[opennlp/*=PIDAT]",
+			defaultFoundry: "opennlp",
+			defaultLayer:   "p",
+			expected: &SimpleTerm{
+				WithFoundryWildcard: &FoundryWildcardTerm{
+					Foundry: "opennlp",
+					Key:     "PIDAT",
+				},
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -228,6 +240,20 @@
 				},
 			},
 		},
+		{
+			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 {
@@ -363,7 +389,7 @@
 				assert.Error(t, err)
 				return
 			}
-			assert.NoError(t, err)
+			assert.NoError(t, err, "Input: %s", tt.input)
 			assert.Equal(t, tt.expected, result)
 		})
 	}