Show More
@@ -24,6 +24,7 b' elements = {' | |||
|
24 | 24 | "symbol": (0, ("symbol",), None), |
|
25 | 25 | "string": (0, ("template",), None), |
|
26 | 26 | "rawstring": (0, ("rawstring",), None), |
|
27 | "template": (0, ("template",), None), | |
|
27 | 28 | "end": (0, None, None), |
|
28 | 29 | } |
|
29 | 30 | |
@@ -35,6 +36,11 b' def tokenize(program, start, end):' | |||
|
35 | 36 | pass |
|
36 | 37 | elif c in "(,)%|": # handle simple operators |
|
37 | 38 | yield (c, None, pos) |
|
39 | elif c in '"\'': # handle quoted templates | |
|
40 | s = pos + 1 | |
|
41 | data, pos = _parsetemplate(program, s, end, c) | |
|
42 | yield ('template', data, s) | |
|
43 | pos -= 1 | |
|
38 | 44 | elif (c in '"\'' or c == 'r' and |
|
39 | 45 | program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings |
|
40 | 46 | if c == 'r': |
@@ -89,7 +95,7 b' def tokenize(program, start, end):' | |||
|
89 | 95 | pos += 1 |
|
90 | 96 | token = 'rawstring' |
|
91 | 97 | else: |
|
92 |
token = ' |
|
|
98 | token = 'template' | |
|
93 | 99 | quote = program[pos:pos + 2] |
|
94 | 100 | s = pos = pos + 2 |
|
95 | 101 | while pos < end: # find closing escaped quote |
@@ -102,6 +108,8 b' def tokenize(program, start, end):' | |||
|
102 | 108 | data = program[s:pos].decode('string-escape') |
|
103 | 109 | except ValueError: # unbalanced escapes |
|
104 | 110 | raise error.ParseError(_("syntax error"), s) |
|
111 | if token == 'template': | |
|
112 | data = _parsetemplate(data, 0, len(data))[0] | |
|
105 | 113 | yield (token, data, s) |
|
106 | 114 | pos += 1 |
|
107 | 115 | break |
@@ -127,27 +135,47 b' def tokenize(program, start, end):' | |||
|
127 | 135 | pos += 1 |
|
128 | 136 | raise error.ParseError(_("unterminated template expansion"), start) |
|
129 | 137 | |
|
130 | def _parsetemplate(tmpl, start, stop): | |
|
138 | def _parsetemplate(tmpl, start, stop, quote=''): | |
|
139 | r""" | |
|
140 | >>> _parsetemplate('foo{bar}"baz', 0, 12) | |
|
141 | ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12) | |
|
142 | >>> _parsetemplate('foo{bar}"baz', 0, 12, quote='"') | |
|
143 | ([('string', 'foo'), ('symbol', 'bar')], 9) | |
|
144 | >>> _parsetemplate('foo"{bar}', 0, 9, quote='"') | |
|
145 | ([('string', 'foo')], 4) | |
|
146 | >>> _parsetemplate(r'foo\"bar"baz', 0, 12, quote='"') | |
|
147 | ([('string', 'foo"'), ('string', 'bar')], 9) | |
|
148 | >>> _parsetemplate(r'foo\\"bar', 0, 10, quote='"') | |
|
149 | ([('string', 'foo\\\\')], 6) | |
|
150 | """ | |
|
131 | 151 | parsed = [] |
|
152 | sepchars = '{' + quote | |
|
132 | 153 | pos = start |
|
133 | 154 | p = parser.parser(elements) |
|
134 | 155 | while pos < stop: |
|
135 |
n = tmpl.find( |
|
|
156 | n = min((tmpl.find(c, pos, stop) for c in sepchars), | |
|
157 | key=lambda n: (n < 0, n)) | |
|
136 | 158 | if n < 0: |
|
137 | 159 | parsed.append(('string', tmpl[pos:stop])) |
|
138 | 160 | pos = stop |
|
139 | 161 | break |
|
162 | c = tmpl[n] | |
|
140 | 163 | bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) |
|
141 | 164 | if bs % 2 == 1: |
|
142 | 165 | # escaped (e.g. '\{', '\\\{', but not '\\{') |
|
143 |
parsed.append(('string', (tmpl[pos:n - 1] + |
|
|
166 | parsed.append(('string', (tmpl[pos:n - 1] + c))) | |
|
144 | 167 | pos = n + 1 |
|
145 | 168 | continue |
|
146 | 169 | if n > pos: |
|
147 | 170 | parsed.append(('string', tmpl[pos:n])) |
|
171 | if c == quote: | |
|
172 | return parsed, n + 1 | |
|
148 | 173 | |
|
149 | 174 | parseres, pos = p.parse(tokenize(tmpl, n + 1, stop)) |
|
150 | 175 | parsed.append(parseres) |
|
176 | ||
|
177 | if quote: | |
|
178 | raise error.ParseError(_("unterminated string"), start) | |
|
151 | 179 | return parsed, pos |
|
152 | 180 | |
|
153 | 181 | def compiletemplate(tmpl, context): |
@@ -182,7 +210,7 b' def getfilter(exp, context):' | |||
|
182 | 210 | |
|
183 | 211 | def gettemplate(exp, context): |
|
184 | 212 | if exp[0] == 'template': |
|
185 |
return compile |
|
|
213 | return [compileexp(e, context, methods) for e in exp[1]] | |
|
186 | 214 | if exp[0] == 'symbol': |
|
187 | 215 | # unlike runsymbol(), here 'symbol' is always taken as template name |
|
188 | 216 | # even if it exists in mapping. this allows us to override mapping |
@@ -215,7 +243,7 b' def runsymbol(context, mapping, key):' | |||
|
215 | 243 | return v |
|
216 | 244 | |
|
217 | 245 | def buildtemplate(exp, context): |
|
218 |
ctmpl = compile |
|
|
246 | ctmpl = [compileexp(e, context, methods) for e in exp[1]] | |
|
219 | 247 | if len(ctmpl) == 1: |
|
220 | 248 | return ctmpl[0] # fast path for string with no template fragment |
|
221 | 249 | return (runtemplate, ctmpl) |
@@ -2541,6 +2541,16 b' Behind the scenes, this will throw Value' | |||
|
2541 | 2541 | abort: template filter 'datefilter' is not compatible with keyword 'author' |
|
2542 | 2542 | [255] |
|
2543 | 2543 | |
|
2544 | Error in nested template: | |
|
2545 | ||
|
2546 | $ hg log -T '{"date' | |
|
2547 | hg: parse error at 2: unterminated string | |
|
2548 | [255] | |
|
2549 | ||
|
2550 | $ hg log -T '{"foo{date|=}"}' | |
|
2551 | hg: parse error at 11: syntax error | |
|
2552 | [255] | |
|
2553 | ||
|
2544 | 2554 | Thrown an error if a template function doesn't exist |
|
2545 | 2555 | |
|
2546 | 2556 | $ hg tip --template '{foo()}\n' |
@@ -2952,7 +2962,7 b' escaped single quotes and errors:' | |||
|
2952 | 2962 | $ hg log -r 2 -T "{if(rev, '{if(rev, r\'foo\')}')}"'\n' |
|
2953 | 2963 | foo |
|
2954 | 2964 | $ hg log -r 2 -T '{if(rev, "{if(rev, \")}")}\n' |
|
2955 |
hg: parse error at |
|
|
2965 | hg: parse error at 21: unterminated string | |
|
2956 | 2966 | [255] |
|
2957 | 2967 | $ hg log -r 2 -T '{if(rev, \"\\"")}\n' |
|
2958 | 2968 | hg: parse error at 11: syntax error |
@@ -3069,6 +3079,14 b' Test string escaping in nested expressio' | |||
|
3069 | 3079 | 3:\x6eo user, \x6eo domai\x6e |
|
3070 | 3080 | 4:\x5c\x786eew bra\x5c\x786ech |
|
3071 | 3081 | |
|
3082 | Test quotes in nested expression are evaluated just like a $(command) | |
|
3083 | substitution in POSIX shells: | |
|
3084 | ||
|
3085 | $ hg log -R a -r 8 -T '{"{"{rev}:{node|short}"}"}\n' | |
|
3086 | 8:95c24699272e | |
|
3087 | $ hg log -R a -r 8 -T '{"{"\{{rev}} \"{node|short}\""}"}\n' | |
|
3088 | {8} "95c24699272e" | |
|
3089 | ||
|
3072 | 3090 | Test recursive evaluation: |
|
3073 | 3091 | |
|
3074 | 3092 | $ hg init r |
General Comments 0
You need to be logged in to leave comments.
Login now