Page MenuHomePhabricator

Scribunto converts template arguments with explicit keys of 0, -1, -2 etc. to numeric keys of frame.args
Open, Needs TriagePublic

Description

I have just noticed that Scribunto converts keys of template arguments like 0=foo, -1=bar or -2=baz into numbers when accessed via frame.args. This seems unexpected, as there is no way to have positional parameters with keys of 0, -1 or -2.

We should probably fix this to only allow keys that could possibly be for positional parameters to be converted to numbers. I don't know if changing the current behaviour would break any existing modules, however.

This came up because of my message here where I suggested that you could use type(key) == 'number' to check whether a parameter was positional or not. It turns out that that is not actually strictly true. :)

I have some example code in my module sandbox:

local p = {}

function p.main(frame)
	local ret = ''
	for k, v in pairs(frame.args) do
		ret = ret .. string.format('key: %s; key type: %s\n', tostring(k), type(k))
	end
	return ret
end

return p

Calling this with {{#invoke:User:Mr. Stradivarius/sandbox|main|-1=foo}} produces key: -1; key type: number.

Event Timeline

It's not Scribunto doing this, this is how PHP associative arrays work. Any key that looks like the standard decimal representation of an integer in range comes out as an integer, while anything else comes out as a string. Hacking around this for template arguments could be done (convert name to a string when appropriate here), but you could run into this in other situations too (e.g. if there's ever a method that returns a table with page titles as keys, pages like https://en.wikipedia.org/wiki/42 would be integer keys unless something similar is done).

And you also leave open the question as to whether {{foo|2=bar}} has a positional parameter or not. And if you say yes, what about {{foo|9=bar}}, or {{foo|42=bar}}, or {{foo|2147483647=bar}}?[1]

If you want to iterate over positional parameters, you'd generally use ipairs() which will stop at the first index that wasn't passed. Or if you want to handle all positive integer keys as "positional", use pairs() and test for that with something like type(key) == 'number' and key > 0.

[1]: Speaking of which, currently things will probably get confused if you try to use integers with absolute value greater than 9007199254740992 (and less than 9223372036854775808): PHP will represent them as integers, but Lua numbers use a double-precision floating-point representaton that can't handle them.