Support corpus mappings

Change-Id: I25e987b0ca668a1cf733424b22edb4f0fca37bf2
diff --git a/config/config.go b/config/config.go
index d245461..cdc1e0f 100644
--- a/config/config.go
+++ b/config/config.go
@@ -24,6 +24,7 @@
 // MappingList represents a list of mapping rules with metadata
 type MappingList struct {
 	ID          string        `yaml:"id"`
+	Type        string        `yaml:"type,omitempty"` // "annotation" (default) or "corpus"
 	Description string        `yaml:"desc,omitempty"`
 	FoundryA    string        `yaml:"foundryA,omitempty"`
 	LayerA      string        `yaml:"layerA,omitempty"`
@@ -32,6 +33,28 @@
 	Mappings    []MappingRule `yaml:"mappings"`
 }
 
+// IsCorpus returns true if the mapping list type is "corpus".
+func (list *MappingList) IsCorpus() bool {
+	return list.Type == "corpus"
+}
+
+// ParseCorpusMappings parses all mapping rules as corpus rules.
+func (list *MappingList) ParseCorpusMappings() ([]*parser.CorpusMappingResult, error) {
+	corpusParser := parser.NewCorpusParser()
+	results := make([]*parser.CorpusMappingResult, len(list.Mappings))
+	for i, rule := range list.Mappings {
+		if rule == "" {
+			return nil, fmt.Errorf("empty corpus mapping rule at index %d in list '%s'", i, list.ID)
+		}
+		result, err := corpusParser.ParseMapping(string(rule))
+		if err != nil {
+			return nil, fmt.Errorf("failed to parse corpus mapping rule %d in list '%s': %w", i, list.ID, err)
+		}
+		results[i] = result
+	}
+	return results, nil
+}
+
 // MappingConfig represents the root configuration containing multiple mapping lists
 type MappingConfig struct {
 	SDK        string        `yaml:"sdk,omitempty"`