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.
--- 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,
--- 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]