en.wiktionary.org

Module:Scribunto - Wiktionary, the free dictionary

From Wiktionary, the free dictionary


The following documentation is located at Module:Scribunto/documentation. [edit]
Useful links: subpage listlinkstransclusionstestcasessandbox

This module provides some utility functions for emulating certain parts of the inner-workings of Scribunto, as well as the underlying PHP-based MediaWiki platform.

They are used to support other modules that deal with template parameters or wikitext parsing (e.g. for template expansion).

Functions

export.php_htmlspecialchars

function export.php_htmlspecialchars(str, quotes)

Lua equivalent of PHP's htmlspecialchars($string), which converts the characters &"'<> to HTML entities.

If the quotes flag is set to "compat", then ' will not be converted, and if it is set to "noquotes", then neither " nor ' will be converted.

export.php_string_equals

function export.php_string_equals(str1, str2)

Lua equivalent of PHP's == operator for strings.

export.php_trim

function export.php_trim(str)

Lua equivalent of PHP's trim($string), which trims "\0", "\t", "\n", "\v", "\r" and " ". This is useful when dealing with template parameters, since the native parser trims them like this.

export.scribunto_parameter_key

function export.scribunto_parameter_key(key, no_trim)

Takes a template or module parameter name as either a string or number, and returns the Scribunto-normalized form (i.e. the key that that parameter would have in a frame.args table). For example, "1" (a string) is normalized to 1 (a number), " foo " is normalized to "foo", and 1.5 (a number) is normalized to "1.5" (a string). Inputs which cannot be normalized (e.g. booleans) return nil.

Strings are trimmed with export.php_trim, unless the no_trim flag is set. If it is, then string parameters are not trimmed, but strings may still be converted to numbers if they do not contain whitespace; this is necessary when normalizing keys into the form received by PHP during callbacks, before any trimming occurs (e.g. in the table of arguments when calling frame:expandTemplates()).

After trimming (if applicable), keys are then converted to numbers if all of the following are true:

  1. They are integers; i.e. no decimals or leading zeroes (e.g. "2", but not "2.0" or "02").
  2. They are ≤ 253 and ≥ -253.
  3. There is no leading sign unless < 0 (e.g. "2" or "-2", but not "+2" or "-0").
  4. They contain no leading or trailing whitespace (which may be present when the no_trim flag is set).

Numbers are converted to strings if either:

  1. They are not integers (e.g. 1.5).
  2. They are > 253 or < -253.

When converted to strings, integers ≤ 263 and ≥ -263 are formatted as integers (i.e. all digits are given), which is the range of PHP's integer precision, though the actual output may be imprecise since Lua's integer precision is > 253 to < -253. All other numbers use the standard formatting output by tostring().

export.scribunto_parameter_value

function export.scribunto_parameter_value(value, named)

Takes a template or module parameter value as either a string, number or boolean, and returns the Scribunto-normalized form (i.e. the value that that parameter would have in a frame.args table), which is always a string. For example, "foo" remains the same, 2 (a number) is normalized to "2" (a string), true is normalized to "1", and false is normalized to "". Inputs which cannot be normalized (e.g. tables) return nil.

By default, returned values are not trimmed, which matches the treatment of unnamed parameters (e.g. bar in {{foo|bar}}). If the named flag is set, then returned values will be trimmed, which matches the treatment of named parameters (e.g. baz in {{foo|bar=baz}}).


local export = {}

local math_module = "Module:math"

local dump = mw.dumpObject
local format = string.format
local gsub = string.gsub
local match = string.match
local php_trim -- defined below
local sub = string.sub
local tonumber = tonumber
local tostring = tostring
local type = type

do
	local php_htmlspecialchars_data
	local function get_php_htmlspecialchars_data()
		php_htmlspecialchars_data, get_php_htmlspecialchars_data = {
			["\""] = "&quot;",
			["&"] = "&amp;",
			["'"] = "&#039;",
			["<"] = "&lt;",
			[">"] = "&gt;",
		}, nil
		return php_htmlspecialchars_data
	end

	--[==[Lua equivalent of PHP's {{code|php|htmlspecialchars($string)}}, which converts the characters `&"'<>` to HTML entities.

	If the `quotes` flag is set to {"compat"}, then `'` will not be converted, and if it is set to {"noquotes"}, then neither `"` nor `'` will be converted.]==]
	function export.php_htmlspecialchars(str, quotes)
		if quotes == nil or quotes == "quotes" then
			quotes = "'\""
		elseif quotes == "compat" then
			quotes = "\""
		elseif quotes == "noquotes" then
			quotes = ""
		else
			local quotes_type = type(quotes)
			error('`quotes` must be "quotes", "compat", "noquotes" or nil; received ' ..
				(quotes_type == "string" and dump(quotes) or "a " .. quotes_type))
		end
		return (gsub(str, "[&<>" .. quotes .. "]", php_htmlspecialchars_data or get_php_htmlspecialchars_data()))
	end
end

do
	local function tonumber_extended(...)
		tonumber_extended = require(math_module).tonumber_extended
		return tonumber_extended(...)
	end

	-- Normalizes a string for use in comparisons which emulate PHP's equals
	-- operator, which coerces certain strings to numbers: those within the
	-- range -2^63 to 2^63 - 1 which don't have decimal points or exponents are
	-- coerced to integers, while any others are coerced to doubles if possible;
	-- otherwise, they remain as strings. PHP and Lua have the same precision
	-- for doubles, but Lua's integer precision range is -2^53 + 1 to 2^53 - 1.
	-- Any integers within Lua's precision, as well as all doubles, are simply
	-- coerced to numbers, but PHP integers outside of Lua's precision are
	-- emulated as normalized strings, with leading 0s and any + sign removed.
	-- The `not_long` flag is used for the second comparator if the first did
	-- not get normalized to a long integer, as PHP will only coerce strings to
	-- integers if it's possible for both comparators.
	local function php_normalize_string(str, not_long)
		local num = tonumber_extended(str, nil, true)
		-- Must be a number that isn't ±infinity, NaN or hexadecimal.
		if not num or match(str, "^%s*[+-]?0[xX]()") then
			return str
		-- If `not_long` is set or `num` is within Lua's precision, return as a
		-- number.
		elseif not_long or num < 9007199254740992 and num > -9007199254740992 then
			return num, "number"
		end
		-- Check if it could be a long integer, and return as a double if not.
		local sign, str_no_0 = match(str, "^%s*([+-]?)0*(%d+)$")
		if not str_no_0 then
			return num, "number"
		end
		-- Otherwise, check if it's a long integer. 2^63 is 9223372036854775808,
		-- so slice off the last 15 digits and deal with the two parts
		-- separately. If the integer value would be too high/low, return as a
		-- string.
		local high = tonumber(sub(str_no_0, 1, -16))
		if high > 9223 then
			return str
		elseif high == 9223 then
			local low = tonumber(sub(str_no_0, -15))
			-- Range is -2^63 to 2^63 - 1 (not symmetrical).
			if low > 372036854775808 or low == 372036854775808 and sign ~= "-" then
				return str
			end
		end
		return (sign == "+" and "" or sign) .. str_no_0, "long integer", num
	end

	--[==[Lua equivalent of PHP's {{code|php|===}} operator for strings.]==]
	function export.php_string_equals(str1, str2)
		if str1 == str2 then
			return true
		end
		local str1, str1_type, str1_num = php_normalize_string(str1)
		if str1 == str2 then
			return true
		elseif str1_type == "long integer" then
			local str2, str2_type = php_normalize_string(str2)
			return str2 == (str2_type == "number" and str1_num or str1)
		elseif str1_type == "number" then
			return str1 == php_normalize_string(str2, true)
		end
		return false
	end
end

--[==[Lua equivalent of PHP's {{code|php|trim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "}. This is useful when dealing with template parameters, since the native parser trims them like this.]==]
function export.php_trim(str)
	return match(str, "%f[^%z\t\n\v\r ].*%f[%z\t\n\v\r ]") or ""
end
php_trim = export.php_trim

--[==[Takes a template or module parameter name as either a string or number, and returns the Scribunto-normalized form (i.e. the key that that parameter would have in a {frame.args} table). For example, {"1"} (a string) is normalized to {1} (a number), {" foo "} is normalized to {"foo"}, and {1.5} (a number) is normalized to {"1.5"} (a string). Inputs which cannot be normalized (e.g. booleans) return {nil}.

Strings are trimmed with {export.php_trim}, unless the `no_trim` flag is set. If it is, then string parameters are not trimmed, but strings may still be converted to numbers if they do not contain whitespace; this is necessary when normalizing keys into the form received by PHP during callbacks, before any trimming occurs (e.g. in the table of arguments when calling {frame:expandTemplates()}).

After trimming (if applicable), keys are then converted to numbers if '''all''' of the following are true:
# They are integers; i.e. no decimals or leading zeroes (e.g. {"2"}, but not {"2.0"} or {"02"}).
# They are ≤ 2{{sup|53}} and ≥ -2{{sup|53}}.
# There is no leading sign unless < 0 (e.g. {"2"} or {"-2"}, but not {"+2"} or {"-0"}).
# They contain no leading or trailing whitespace (which may be present when the `no_trim` flag is set).

Numbers are converted to strings if '''either''':
# They are not integers (e.g. {1.5}).
# They are > 2{{sup|53}} or < -2{{sup|53}}.

When converted to strings, integers ≤ 2{{sup|63}} and ≥ -2{{sup|63}} are formatted as integers (i.e. all digits are given), which is the range of PHP's integer precision, though the actual output may be imprecise since Lua's integer precision is > 2{{sup|53}} to < -2{{sup|53}}. All other numbers use the standard formatting output by {tostring()}.]==]
function export.scribunto_parameter_key(key, no_trim)
	local key_type = type(key)
	if key_type == "string" then
		if not no_trim then
			key = php_trim(key)
		end
		if match(key, "^()-?[1-9]%d*$") then
			local num = tonumber(key)
			-- Lua integers are only precise to 2^53 - 1, so specifically check
			-- for 2^53 and -2^53 as strings, since a numerical comparison won't
			-- work as it can't distinguish 2^53 from 2^53 + 1.
			return (
				num <= 9007199254740991 and num >= -9007199254740991 or
				key == "9007199254740992" or
				key == "-9007199254740992"
			) and num or key
		end
		return key == "0" and 0 or key
	elseif key_type == "number" then
		-- No special handling needed for inf or NaN.
		return key % 1 == 0 and (
			key <= 9007199254740992 and key >= -9007199254740992 and key or
			key <= 9223372036854775808 and key >= -9223372036854775808 and format("%d", key)
		) or tostring(key)
	end
	return nil
end

--[==[Takes a template or module parameter value as either a string, number or boolean, and returns the Scribunto-normalized form (i.e. the value that that parameter would have in a {frame.args} table), which is always a string. For example, {"foo"} remains the same, {2} (a number) is normalized to {"2"} (a string), {true} is normalized to {"1"}, and {false} is normalized to {""}. Inputs which cannot be normalized (e.g. tables) return {nil}.

By default, returned values are not trimmed, which matches the treatment of unnamed parameters (e.g. `bar` in {{tl|<nowiki/>foo|bar}}). If the `named` flag is set, then returned values will be trimmed, which matches the treatment of named parameters (e.g. `baz` in {{tl|<nowiki/>foo|bar=baz}}).]==]
function export.scribunto_parameter_value(value, named)
	local value_type = type(value)
	if value_type == "string" then
		return named and php_trim(value) or value
	elseif value_type == "number" then
		return tostring(value)
	elseif value_type == "boolean" then
		return value and "1" or ""
	end
	return nil
end

return export