comparison mercurial/templater.py @ 25785:f976b7dc5e7b

templater: unify "string" and "rawstring" "rawstring" was introduced by 5ab28a2e9962, but it's no longer necessary because c1975809a6b5 and fd5bc660c9f0 changed the way of processing string literals. This patch moves string decoding to the parsing phase as it was before: ('rawstring', s) -> ('string', s) ('string', s) -> ('string', s.decode('string-escape'))
author Yuya Nishihara <yuya@tcha.org>
date Sat, 20 Jun 2015 18:24:11 +0900
parents 33e613687dab
children f8f7ae0f4d98
comparison
equal deleted inserted replaced
25784:33e613687dab 25785:f976b7dc5e7b
20 "|": (5, None, ("|", 5)), 20 "|": (5, None, ("|", 5)),
21 "%": (6, None, ("%", 6)), 21 "%": (6, None, ("%", 6)),
22 ")": (0, None, None), 22 ")": (0, None, None),
23 "integer": (0, ("integer",), None), 23 "integer": (0, ("integer",), None),
24 "symbol": (0, ("symbol",), None), 24 "symbol": (0, ("symbol",), None),
25 "rawstring": (0, ("rawstring",), None), 25 "string": (0, ("string",), None),
26 "template": (0, ("template",), None), 26 "template": (0, ("template",), None),
27 "end": (0, None, None), 27 "end": (0, None, None),
28 } 28 }
29 29
30 def tokenize(program, start, end): 30 def tokenize(program, start, end):
48 d = program[pos] 48 d = program[pos]
49 if d == '\\': # skip over escaped characters 49 if d == '\\': # skip over escaped characters
50 pos += 2 50 pos += 2
51 continue 51 continue
52 if d == c: 52 if d == c:
53 yield ('rawstring', program[s:pos], s) 53 yield ('string', program[s:pos], s)
54 break 54 break
55 pos += 1 55 pos += 1
56 else: 56 else:
57 raise error.ParseError(_("unterminated string"), s) 57 raise error.ParseError(_("unterminated string"), s)
58 elif c.isdigit() or c == '-': 58 elif c.isdigit() or c == '-':
81 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}] 81 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
82 # ~~~~~~~~ 82 # ~~~~~~~~
83 # escaped quoted string 83 # escaped quoted string
84 if c == 'r': 84 if c == 'r':
85 pos += 1 85 pos += 1
86 token = 'rawstring' 86 token = 'string'
87 else: 87 else:
88 token = 'template' 88 token = 'template'
89 quote = program[pos:pos + 2] 89 quote = program[pos:pos + 2]
90 s = pos = pos + 2 90 s = pos = pos + 2
91 while pos < end: # find closing escaped quote 91 while pos < end: # find closing escaped quote
134 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"') 134 >>> _parsetemplate('foo"{bar}', 0, 9, quote='"')
135 ([('string', 'foo')], 4) 135 ([('string', 'foo')], 4)
136 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"') 136 >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"')
137 ([('string', 'foo"'), ('string', 'bar')], 9) 137 ([('string', 'foo"'), ('string', 'bar')], 9)
138 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"') 138 >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"')
139 ([('string', 'foo\\\\')], 6) 139 ([('string', 'foo\\')], 6)
140 """ 140 """
141 parsed = [] 141 parsed = []
142 sepchars = '{' + quote 142 sepchars = '{' + quote
143 pos = start 143 pos = start
144 p = parser.parser(elements) 144 p = parser.parser(elements)
145 while pos < stop: 145 while pos < stop:
146 n = min((tmpl.find(c, pos, stop) for c in sepchars), 146 n = min((tmpl.find(c, pos, stop) for c in sepchars),
147 key=lambda n: (n < 0, n)) 147 key=lambda n: (n < 0, n))
148 if n < 0: 148 if n < 0:
149 parsed.append(('string', tmpl[pos:stop])) 149 parsed.append(('string', tmpl[pos:stop].decode('string-escape')))
150 pos = stop 150 pos = stop
151 break 151 break
152 c = tmpl[n] 152 c = tmpl[n]
153 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) 153 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
154 if bs % 2 == 1: 154 if bs % 2 == 1:
155 # escaped (e.g. '\{', '\\\{', but not '\\{') 155 # escaped (e.g. '\{', '\\\{', but not '\\{')
156 parsed.append(('string', (tmpl[pos:n - 1] + c))) 156 parsed.append(('string',
157 tmpl[pos:n - 1].decode('string-escape') + c))
157 pos = n + 1 158 pos = n + 1
158 continue 159 continue
159 if n > pos: 160 if n > pos:
160 parsed.append(('string', tmpl[pos:n])) 161 parsed.append(('string', tmpl[pos:n].decode('string-escape')))
161 if c == quote: 162 if c == quote:
162 return parsed, n + 1 163 return parsed, n + 1
163 164
164 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop)) 165 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop))
165 parsed.append(parseres) 166 parsed.append(parseres)
210 211
211 def runinteger(context, mapping, data): 212 def runinteger(context, mapping, data):
212 return int(data) 213 return int(data)
213 214
214 def runstring(context, mapping, data): 215 def runstring(context, mapping, data):
215 return data.decode("string-escape")
216
217 def runrawstring(context, mapping, data):
218 return data 216 return data
219 217
220 def runsymbol(context, mapping, key): 218 def runsymbol(context, mapping, key):
221 v = mapping.get(key) 219 v = mapping.get(key)
222 if v is None: 220 if v is None:
657 655
658 # methods to interpret function arguments or inner expressions (e.g. {_(x)}) 656 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
659 exprmethods = { 657 exprmethods = {
660 "integer": lambda e, c: (runinteger, e[1]), 658 "integer": lambda e, c: (runinteger, e[1]),
661 "string": lambda e, c: (runstring, e[1]), 659 "string": lambda e, c: (runstring, e[1]),
662 "rawstring": lambda e, c: (runrawstring, e[1]),
663 "symbol": lambda e, c: (runsymbol, e[1]), 660 "symbol": lambda e, c: (runsymbol, e[1]),
664 "template": buildtemplate, 661 "template": buildtemplate,
665 "group": lambda e, c: compileexp(e[1], c, exprmethods), 662 "group": lambda e, c: compileexp(e[1], c, exprmethods),
666 # ".": buildmember, 663 # ".": buildmember,
667 "|": buildfilter, 664 "|": buildfilter,