* parser.cs: Allow creating a regular expression using {,n} as the
[mono.git] / mcs / class / System / System.Text.RegularExpressions / replace.cs
1 //\r
2 // assembly:    System\r
3 // namespace:   System.Text.RegularExpressions\r
4 // file:        replace.cs\r
5 //\r
6 // author:      Dan Lewis (dlewis@gmx.co.uk)\r
7 //              (c) 2002\r
8 \r
9 using System;\r
10 using System.Text;\r
11 using System.Collections;\r
12 \r
13 using Parser = System.Text.RegularExpressions.Syntax.Parser;\r
14 \r
15 namespace System.Text.RegularExpressions {\r
16 \r
17         class ReplacementEvaluator {\r
18                 public static string Evaluate (string replacement, Match match) {\r
19                         ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);\r
20                         return ev.Evaluate (match);\r
21                 }\r
22 \r
23                 public ReplacementEvaluator (Regex regex, string replacement) {\r
24                         this.regex = regex;\r
25                         terms = new ArrayList ();\r
26                         Compile (replacement);\r
27                 }\r
28 \r
29                 public string Evaluate (Match match) {\r
30                         StringBuilder result = new StringBuilder ();\r
31                         foreach (Term term in terms)\r
32                                 result.Append (term.GetResult (match));\r
33 \r
34                         return result.ToString ();\r
35                 }\r
36 \r
37                 // private\r
38 \r
39                 private void Compile (string replacement) {\r
40                         replacement = Parser.Unescape (replacement);\r
41                         string literal = "";\r
42 \r
43                         int ptr = 0;\r
44                         char c;\r
45                         Term term = null;\r
46                         while (ptr < replacement.Length) {\r
47                                 c = replacement[ptr ++];\r
48 \r
49                                 if (c == '$') {\r
50                                         if (replacement[ptr] != '$') \r
51                                                 term = CompileTerm (replacement, ref ptr);\r
52                                         else\r
53                                                 ++ ptr;\r
54                                 }\r
55 \r
56                                 if (term != null) {\r
57                                         term.Literal = literal;\r
58                                         terms.Add (term);\r
59 \r
60                                         term = null;\r
61                                         literal = "";\r
62                                 }\r
63                                 else\r
64                                         literal += c;\r
65                         }\r
66 \r
67                         if (term == null && literal.Length > 0) {\r
68                                 terms.Add (new Term (literal));\r
69                         }\r
70                 }\r
71 \r
72                 private Term CompileTerm (string str, ref int ptr) {\r
73                         char c = str[ptr];\r
74 \r
75                         if (Char.IsDigit (c)) {         // numbered group\r
76                                 int n = Parser.ParseDecimal (str, ref ptr);\r
77                                 if (n < 0 || n > regex.GroupCount)\r
78                                         throw new ArgumentException ("Bad group number.");\r
79                                 \r
80                                 return new Term (TermOp.Match, n);\r
81                         }\r
82                         \r
83                         ++ ptr;\r
84 \r
85                         switch (c) {\r
86                         case '{': {                     // named group\r
87                                 string name = Parser.ParseName (str, ref ptr);\r
88                                 if (str[ptr ++] != '}' || name == null)\r
89                                         throw new ArgumentException ("Bad group name.");\r
90                                 \r
91                                 int n = regex.GroupNumberFromName (name);\r
92                                 \r
93                                 if (n < 0)\r
94                                         throw new ArgumentException ("Bad group name.");\r
95 \r
96                                 return new Term (TermOp.Match, n);\r
97                         }\r
98 \r
99                         case '&':                       // entire match\r
100                                 return new Term (TermOp.Match, 0);\r
101 \r
102                         case '`':                       // text before match\r
103                                 return new Term (TermOp.PreMatch, 0);\r
104 \r
105                         case '\'':                      // text after match\r
106                                 return new Term (TermOp.PostMatch, 0);\r
107 \r
108                         case '+':                       // last group\r
109                                 return new Term (TermOp.Match, regex.GroupCount - 1);\r
110 \r
111                         case '_':                       // entire text\r
112                                 return new Term (TermOp.All, 0);\r
113 \r
114                         default:\r
115                                 throw new ArgumentException ("Bad replacement pattern.");\r
116                         }\r
117                 }\r
118 \r
119                 private Regex regex;\r
120                 private ArrayList terms;\r
121 \r
122                 private enum TermOp {\r
123                         None,                           // no action\r
124                         Match,                          // input within group\r
125                         PreMatch,                       // input before group\r
126                         PostMatch,                      // input after group\r
127                         All                             // entire input\r
128                 }\r
129 \r
130                 private class Term {\r
131                         public Term (TermOp op, int arg) {\r
132                                 this.op = op;\r
133                                 this.arg = arg;\r
134                                 this.literal = "";\r
135                         }\r
136 \r
137                         public Term (string literal) {\r
138                                 this.op = TermOp.None;\r
139                                 this.arg = 0;\r
140                                 this.literal = literal;\r
141                         }\r
142 \r
143                         public string Literal {\r
144                                 set { literal = value; }\r
145                         }\r
146 \r
147                         public string GetResult (Match match) {\r
148                                 Group group = match.Groups[arg];\r
149                         \r
150                                 switch (op) {\r
151                                 case TermOp.None:\r
152                                         return literal;\r
153 \r
154                                 case TermOp.Match:\r
155                                         return literal + group.Value;\r
156 \r
157                                 case TermOp.PreMatch:\r
158                                         return literal + group.Text.Substring (0, group.Index);\r
159 \r
160                                 case TermOp.PostMatch:\r
161                                         return literal + group.Text.Substring (group.Index + group.Length);\r
162 \r
163                                 case TermOp.All:\r
164                                         return literal + group.Text;\r
165                                 }\r
166 \r
167                                 return "";\r
168                         }\r
169                 \r
170                         public TermOp op;               // term type\r
171                         public int arg;                 // group argument\r
172                         public string literal;          // literal to prepend\r
173 \r
174                         public override string ToString () {\r
175                                 return op.ToString () + "(" + arg + ") " + literal;\r
176                         }\r
177                 }\r
178         }\r
179 }\r