# HG changeset patch # User Yuya Nishihara # Date 1430480635 -32400 # Node ID 829faf8ab605a87625ba3cdfc8aa7eeae4777737 # Parent 9668c1a433b3c8e07f3f86ecf0369d5a55b198be templater: tokenize decimal integer literal (issue4638) (BC) Before this patch, we had to quote integer literals to pass to template functions. It was error-prone, so we should allow "word(0, x)" syntax. Currently only decimal integers are allowed. It's easy to support 0x, 0b and 0 prefixes, but I don't think they are useful. This patch assumes that template keywords and names defined in map files do not start with digits, except for positional variables seen in the schemes extension. diff -r 9668c1a433b3 -r 829faf8ab605 mercurial/templater.py --- a/mercurial/templater.py Sat May 02 18:05:04 2015 +0900 +++ b/mercurial/templater.py Fri May 01 20:43:55 2015 +0900 @@ -20,6 +20,7 @@ "|": (5, None, ("|", 5)), "%": (6, None, ("%", 6)), ")": (0, None, None), + "integer": (0, ("integer",), None), "symbol": (0, ("symbol",), None), "string": (0, ("string",), None), "rawstring": (0, ("rawstring",), None), @@ -59,6 +60,20 @@ pos += 1 else: raise error.ParseError(_("unterminated string"), s) + elif c.isdigit() or c == '-': + s = pos + if c == '-': # simply take negate operator as part of integer + pos += 1 + if pos >= end or not program[pos].isdigit(): + raise error.ParseError(_("integer literal without digits"), s) + pos += 1 + while pos < end: + d = program[pos] + if not d.isdigit(): + break + pos += 1 + yield ('integer', program[s:pos], s) + pos -= 1 elif c.isalnum() or c in '_': s = pos pos += 1 @@ -135,6 +150,9 @@ return context._load(exp[1]) raise error.ParseError(_("expected template specifier")) +def runinteger(context, mapping, data): + return int(data) + def runstring(context, mapping, data): return data.decode("string-escape") @@ -567,6 +585,7 @@ # methods to interpret function arguments or inner expressions (e.g. {_(x)}) exprmethods = { + "integer": lambda e, c: (runinteger, e[1]), "string": lambda e, c: (runstring, e[1]), "rawstring": lambda e, c: (runrawstring, e[1]), "symbol": lambda e, c: (runsymbol, e[1]), @@ -579,6 +598,7 @@ # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"}) methods = exprmethods.copy() +methods["integer"] = exprmethods["symbol"] # '{1}' as variable funcs = { "date": date, diff -r 9668c1a433b3 -r 829faf8ab605 tests/test-command-template.t --- a/tests/test-command-template.t Sat May 02 18:05:04 2015 +0900 +++ b/tests/test-command-template.t Fri May 01 20:43:55 2015 +0900 @@ -2291,6 +2291,39 @@ hg: parse error: date expects a date information [255] +Test integer literal: + + $ hg log -Ra -r0 -T '{(0)}\n' + 0 + $ hg log -Ra -r0 -T '{(123)}\n' + 123 + $ hg log -Ra -r0 -T '{(-4)}\n' + -4 + $ hg log -Ra -r0 -T '{(-)}\n' + hg: parse error at 2: integer literal without digits + [255] + $ hg log -Ra -r0 -T '{(-a)}\n' + hg: parse error at 2: integer literal without digits + [255] + +top-level integer literal is interpreted as symbol (i.e. variable name): + + $ hg log -Ra -r0 -T '{1}\n' + + $ hg log -Ra -r0 -T '{if("t", "{1}")}\n' + + $ hg log -Ra -r0 -T '{1|stringify}\n' + + +unless explicit symbol is expected: + + $ hg log -Ra -r0 -T '{desc|1}\n' + hg: parse error: expected a symbol, got 'integer' + [255] + $ hg log -Ra -r0 -T '{1()}\n' + hg: parse error: expected a symbol, got 'integer' + [255] + Test string escaping: $ hg log -R latesttag -r 0 --template '>\n<>\\n<{if(rev, "[>\n<>\\n<]")}>\n<>\\n<\n' @@ -2737,8 +2770,13 @@ hg: parse error: word expects two or three arguments, got 7 [255] +Test word for integer literal + + $ hg log -R a --template "{word(2, desc)}\n" -r0 + line + Test word for invalid numbers - $ hg log -Gv -R a --template "{word(2, desc)}" + $ hg log -Gv -R a --template "{word('a', desc)}" hg: parse error: Use strings like '3' for numbers passed to word function [255]