142 def _ParseTemplate(self, source):
143 """Converts the template string into a Template object."""
144
145 items = []
146 holes = []
147
148
149 pos = 0
150 while True:
151 match = Emitter._SUBST_RE.search(source, pos)
152 if not match:
153 items.append(source[pos:])
154 break
155 text_fragment = source[pos:match.start()]
156 if text_fragment:
157 items.append(text_fragment)
158 pos = match.end()
159 term = match.group()
160 name = match.group(1) or match.group(2)
161 if name:
162 item = Emitter.Lookup(name, term, term)
163 items.append(item)
164 continue
165 name = match.group(3) or match.group(4)
166 if name:
167 item = Emitter.Lookup(name, term, term)
168 items.append(item)
169 holes.append(name)
170 continue
171 name = match.group(5) or match.group(6)
172 if name:
173 item = Emitter.Lookup(name, term, '')
174 items.append(item)
175 holes.append(name)
176 continue
177 name = match.group(7)
178 if name:
179
180
181 paren_count = 1
182 curr_pos = pos
183 while curr_pos < len(source):
184 if source[curr_pos] == ')':
185 paren_count -= 1
186 if paren_count == 0:
187 break
188 elif source[curr_pos] == '(':
189
190 paren_count += 1
191 curr_pos += 1
192 if curr_pos == len(source):
193
194
195 items.append(term)
196 continue
197 matched_template = self._ParseTemplate(source[pos:curr_pos])
198 if len(matched_template._holes) > 0:
199 raise RuntimeError(
200 '$#NAME syntax cannot contains holes in its arguments')
201 item = Emitter.Lookup(name, term, term, matched_template)
202 items.append(item)
203
204 pos = curr_pos + 1
205 continue
206 raise RuntimeError('Unexpected group')
207
208 if len(holes) != len(set(holes)):
209 raise RuntimeError('Cannot have repeated holes %s' % holes)
210 return Emitter.Template(items, holes)
211