|
|
| Line 1: |
Line 1: |
| --- Arguments invocation argument extractor for Scribunto modules. | | -- This module provides easy processing of arguments passed to Scribunto from |
| -- It is intended for use by other Lua modules, and should not be | | -- #invoke. It is intended for use by other Lua modules, and should not be |
| -- called from an invocation (`#invoke`) directly. | | -- called from #invoke directly. |
| --
| | |
| -- This module supports the following features:
| | local libraryUtil = require('libraryUtil') |
| -- * Trimming and blank argument removal.
| | local checkType = libraryUtil.checkType |
| -- * Argument inheritance between child and parent frames.
| | |
| -- * Argument extraction for external modules and console input.
| |
| -- * Options to customise argument extraction behaviour.
| |
| --
| |
| -- @script arguments
| |
| -- @release stable
| |
| -- @note The `args` table from the @{arguments.getArgs}
| |
| -- function is a metatable for performance reasons.
| |
| -- Thus, the table will not permit Lua table methods
| |
| -- such as `#args`, @{next|next(args)}, and @{table}
| |
| -- library functions.
| |
| -- @note This module will eventually be adapted as a
| |
| -- library in [[mw:gerrit:q/158323|MediaWiki
| |
| -- core]], called as `require('getArgs')`. The core
| |
| -- library will remove `options.parentOnly`.
| |
| -- @author [[wikipedia:User:Mr. Stradivarius|Mr. Stradivarius]] (Wikipedia)
| |
| -- @author [[wikipedia:User:Anomie|Anomie]] (Wikipedia)
| |
| -- @author [[wikipedia:User:Jackmcbarn|Jackmcbarn]] (Wikipedia)
| |
| -- @author [[User:Dessamator|Dessamator]]
| |
| -- @author [[User:DarthKitty|DarthKitty]]
| |
| -- @attribution [[wikipedia:Module:Arguments|Module:Arguments]] (Wikipedia)
| |
| -- @see [[wikipedia:Module:Arguments|Original module on Wikipedia]]
| |
| -- @see [[Module:Arguments/testcases|Test cases for this module]]
| |
| local arguments = {} | | local arguments = {} |
|
| |
|
| -- Module dependencies. | | -- Generate four different tidyVal functions, so that we don't have to check the |
| local i18n = require('Dev:I18n').loadMessages('Arguments')
| | -- options every time we call it. |
| local util = require('libraryUtil')
| |
| local checkType = util.checkType
| |
| | |
| -- Four different value tidying functions.
| |
| -- This way, we don't have to check the options every time we call them.
| |
|
| |
|
| --- Default value tidying function.
| |
| -- Trims parameter values automatically if they are defined strings.
| |
| -- Treats blank strings as `nil`.
| |
| -- @function tidyValDefault
| |
| -- @param {string|number} key MediaWiki parameter key.
| |
| -- @param {string|nil} val MediaWiki parameter value,
| |
| -- or nil if `key` is an empty string or nil.
| |
| -- @local
| |
| local function tidyValDefault(key, val) | | local function tidyValDefault(key, val) |
| if type(val) == 'string' then
| | if type(val) == 'string' then |
| val = val:match('^%s*(.-)%s*$')
| | val = val:match('^%s*(.-)%s*$') |
| if val == '' then
| | if val == '' then |
| return nil
| | return nil |
| else
| | else |
| return val
| | return val |
| end
| | end |
| else
| | else |
| return val
| | return val |
| end
| | end |
| end | | end |
|
| |
|
| --- Value tidying function that trims values.
| |
| -- Trims parameter values automatically if they are defined strings.
| |
| -- @function tidyValTrimOnly
| |
| -- @param {string|number} key MediaWiki parameter key.
| |
| -- @param {string|nil} val MediaWiki parameter value.
| |
| -- @local
| |
| local function tidyValTrimOnly(key, val) | | local function tidyValTrimOnly(key, val) |
| if type(val) == 'string' then
| | if type(val) == 'string' then |
| return val:match('^%s*(.-)%s*$')
| | return val:match('^%s*(.-)%s*$') |
| else
| | else |
| return val
| | return val |
| end
| | end |
| end | | end |
|
| |
|
| --- Value tidying function that removes blanks.
| |
| -- Removes blank values from the arguments table.
| |
| -- @function tidyValRemoveBlanksOnly
| |
| -- @param {string|number} key MediaWiki parameter key.
| |
| -- @param {string|nil} val MediaWiki parameter value,
| |
| -- or nil if `key` is whitespace or nil.
| |
| -- @local
| |
| local function tidyValRemoveBlanksOnly(key, val) | | local function tidyValRemoveBlanksOnly(key, val) |
| if type(val) == 'string' then
| | if type(val) == 'string' then |
| if val:find('%S') then
| | if val:find('%S') then |
| return val
| | return val |
| else
| | else |
| return nil
| | return nil |
| end
| | end |
| else
| | else |
| return val
| | return val |
| end
| | end |
| end | | end |
|
| |
|
| --- Value tidying function that returns original value.
| |
| -- Effectively a NOOP function that does no value processing.
| |
| -- @function tidyValNoChange
| |
| -- @param {string|number} key MediaWiki parameter key.
| |
| -- @param {string|nil} val MediaWiki parameter value.
| |
| -- @local
| |
| local function tidyValNoChange(key, val) | | local function tidyValNoChange(key, val) |
| return val
| | return val |
| end | | end |
|
| |
|
| --- Parent template title match checker.
| |
| -- @function matchesTitle
| |
| -- @param {string|number|nil} given Local prefixed page
| |
| -- title, or MediaWiki article ID (`wgArticleId`).
| |
| -- @param {string} title Title of parent template.
| |
| -- @return {boolean} Whether the `given` ID/title matches
| |
| -- the title of the parent template.
| |
| local function matchesTitle(given, title) | | local function matchesTitle(given, title) |
| local tp = type( given )
| | local tp = type( given ) |
| return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title
| | return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title |
| end | | end |
|
| |
|
| --- Default argument translation metatable.
| |
| -- @table translate_mt
| |
| -- @local
| |
| local translate_mt = { __index = function(t, k) return k end } | | local translate_mt = { __index = function(t, k) return k end } |
|
| |
|
| --- Main argument extraction utility.
| |
| -- Arguments are memoized once fetched for optimal performance,
| |
| -- as with the `frame.args` metatable in Scribunto core.
| |
| --
| |
| -- The default argument lookup behaviour uses the child frame arguments
| |
| -- first, then the parent frame arguments. There are numerous frame
| |
| -- options to change this behaviour.
| |
| --
| |
| -- The default value tidying behaviour trims parameter values if they
| |
| -- are defined strings and treats blank strings as `nil`. This can be
| |
| -- customised in the `getArgs` options.
| |
| --
| |
| -- @param {frame|table} frame Scribunto frame object or
| |
| -- Lua arguments table, passed from an invocation
| |
| -- or Lua logic such as `frame:getParent()`.
| |
| -- If this parameter does not have an `args` field
| |
| -- and a `getParent` method, `frame` is assumed
| |
| -- to be a Lua arguments table, such as the
| |
| -- arguments from a named arguments call.
| |
| -- @param[opt] {table} options Extraction/processing options.
| |
| -- @param[opt] {boolean} options.trim
| |
| -- Whether to trim the blank arguments present in
| |
| -- the arguments table. Accepts `false` only.
| |
| -- Default: `true`.
| |
| -- @param[opt] {boolean} options.removeBlanks
| |
| -- Whether to remove blank arguments from the
| |
| -- arguments table. Does not shift sequential
| |
| -- arguments removed by the processing stage.
| |
| -- Accepts `false` only. Default: `true`.
| |
| -- @param[opt] {function} options.valueFunc
| |
| -- Custom value tidying function for use if the
| |
| -- `trim` and `removeBlanks` options don't cover
| |
| -- the developer's argument processing use case.
| |
| -- @param[opt] {boolean} options.frameOnly
| |
| -- Only read arguments from child frame (the
| |
| -- `frame` parameter - usually invocation frame).
| |
| -- @param[opt] {boolean} options.parentOnly
| |
| -- Only read arguments from `frame` parent (the
| |
| -- `frame` parameter - usually template frame).
| |
| -- @param[opt] {boolean} options.parentFirst
| |
| -- Argument lookup in the `frame` parent first,
| |
| -- prioritised over the invocation frame arguments.
| |
| -- @param[opt] {table} options.wrappers
| |
| -- Individual value or array of values, listing
| |
| -- wrapper title name(s) or article ID(s) to permit
| |
| -- parent argument lookup from.
| |
| -- @param[opt] {string|number} options.wrapper
| |
| -- Alias of `options.wrappers` - contains title
| |
| -- name or article ID to permit parent argument
| |
| -- lookup from.
| |
| -- @param[opt] {boolean} options.readOnly
| |
| -- Whether to restrict write permissions to the
| |
| -- arguments table. When set to a truthy value,
| |
| -- an error will be thrown on any write attempt.
| |
| -- @param[opt] {boolean} options.noOverwrite
| |
| -- Whether to restrict overwrite attempts on
| |
| -- existing argument keys in the arguments table.
| |
| -- When set to a truthy value, an error will be
| |
| -- thrown on any write attempt that would result
| |
| -- in an existing argument being overwritten.
| |
| -- @param[opt] {table} options.translate
| |
| -- Map of parameter name aliases to their canonical
| |
| -- argument parameter names.
| |
| -- @param[opt] {table} options.backtranslate
| |
| -- Map of canonical parameter names to their
| |
| -- argument parameter aliases.
| |
| -- Supersedes `options.translate` if both options
| |
| -- are in use.
| |
| -- @error[opt,317] 'bad value assigned to option "valueFunc"
| |
| -- (function expected, got $type)'
| |
| -- @error[opt,407] 'could not write to argument table key "$key";
| |
| -- the table is read-only'
| |
| -- @error[opt,409] 'could not write to argument table key "$key";
| |
| -- overwriting existing arguments is not permitted'
| |
| -- @return {table} Arguments extracted from invocation.
| |
| -- The argument data is embedded as a metatable in
| |
| -- the exported table and cannot be accessed with
| |
| -- the `#` operator or @{table} library methods.
| |
| -- However, the exported table can be written to if
| |
| -- the `options.readOnly` flag parameter is not
| |
| -- truthy.
| |
| -- @usage
| |
| --
| |
| -- local getArgs = require('Module:Arguments').getArgs
| |
| -- function p.main(frame)
| |
| -- local args = getArgs(frame, {
| |
| -- wrapper = 'Template:<TEMPLATE>'
| |
| -- })
| |
| -- -- Use the args table here.
| |
| -- -- A common paradigm is `return p._main(args)`.
| |
| -- -- This allows other Lua modules to access the
| |
| -- -- main logic in a performant manner without a
| |
| -- -- frame object.
| |
| -- end
| |
| --
| |
| -- @note Reference tags in the form of `<ref>` will
| |
| -- generate phantom references when calling the
| |
| -- @{pairs} iterator on the arguments table,
| |
| -- **IF** the `<ref>` tag does not appear in the
| |
| -- dependent module's wikitext output.
| |
| function arguments.getArgs(frame, options) | | function arguments.getArgs(frame, options) |
| checkType('getArgs', 1, frame, 'table', true)
| | checkType('getArgs', 1, frame, 'table', true) |
| checkType('getArgs', 2, options, 'table', true)
| | checkType('getArgs', 2, options, 'table', true) |
| frame = frame or {}
| | frame = frame or {} |
| options = options or {}
| | options = options or {} |
|
| |
|
| -- Set up argument translation.
| | --[[ |
| options.translate = options.translate or {}
| | -- Set up argument translation. |
| if getmetatable(options.translate) == nil then
| | --]] |
| setmetatable(options.translate, translate_mt)
| | options.translate = options.translate or {} |
| end
| | if getmetatable(options.translate) == nil then |
| if options.backtranslate == nil then
| | setmetatable(options.translate, translate_mt) |
| options.backtranslate = {}
| | end |
| for k,v in pairs(options.translate) do
| | if options.backtranslate == nil then |
| options.backtranslate[v] = k
| | options.backtranslate = {} |
| end
| | for k,v in pairs(options.translate) do |
| end
| | options.backtranslate[v] = k |
| if options.backtranslate and getmetatable(options.backtranslate) == nil then
| | end |
| setmetatable(options.backtranslate, {
| | end |
| __index = function(t, k)
| | if options.backtranslate and getmetatable(options.backtranslate) == nil then |
| if options.translate[k] ~= k then
| | setmetatable(options.backtranslate, { |
| return nil
| | __index = function(t, k) |
| else
| | if options.translate[k] ~= k then |
| return k
| | return nil |
| end
| | else |
| end
| | return k |
| })
| | end |
| end
| | end |
| | }) |
| | end |
|
| |
|
| -- Get the argument tables. If we were passed a valid frame object,
| | --[[ |
| -- get the frame arguments (fargs) and the parent frame arguments
| | -- Get the argument tables. If we were passed a valid frame object, get the |
| -- (pargs), depending on the options set and on the parent frame's
| | -- frame arguments (fargs) and the parent frame arguments (pargs), depending |
| -- availability. If we weren't passed a valid frame object, we are
| | -- on the options set and on the parent frame's availability. If we weren't |
| -- being called from another Lua module or from the debug console,
| | -- passed a valid frame object, we are being called from another Lua module |
| -- so assume that we were passed a table of args directly, and
| | -- or from the debug console, so assume that we were passed a table of args |
| -- assign it to a new variable (luaArgs).
| | -- directly, and assign it to a new variable (luaArgs). |
| local fargs, pargs, luaArgs
| | --]] |
| options.wrappers = options.wrappers or options.wrapper
| | local fargs, pargs, luaArgs |
| if
| | if type(frame.args) == 'table' and type(frame.getParent) == 'function' then |
| type(frame.args) == 'table' and
| | if options.wrappers then |
| type(frame.getParent) == 'function'
| | --[[ |
| then
| | -- The wrappers option makes Module:Arguments look up arguments in |
| -- The wrappers option makes Module:Arguments look up
| | -- either the frame argument table or the parent argument table, but |
| -- arguments in either the frame argument table or the
| | -- not both. This means that users can use either the #invoke syntax |
| -- parent argument table, but not both. This means that
| | -- or a wrapper template without the loss of performance associated |
| -- users can use either the #invoke syntax or a wrapper
| | -- with looking arguments up in both the frame and the parent frame. |
| -- template without the loss of performance associated
| | -- Module:Arguments will look up arguments in the parent frame |
| -- with looking arguments up in both the frame and the
| | -- if it finds the parent frame's title in options.wrapper; |
| -- parent frame.
| | -- otherwise it will look up arguments in the frame object passed |
| -- The arguments will be fetched from the parent frame if
| | -- to getArgs. |
| -- the parent frame's title is present in options.wrapper;
| | --]] |
| -- otherwise it will look up arguments in the frame object
| | local parent = frame:getParent() |
| -- passed to getArgs.
| | if not parent then |
| if options.wrappers then
| | fargs = frame.args |
| local parent = frame:getParent()
| | else |
| if not parent then
| | local title = parent:getTitle():gsub('/sandbox$', '') |
| fargs = frame.args
| | local found = false |
| else
| | if matchesTitle(options.wrappers, title) then |
| local title = parent:getTitle():gsub('/sandbox$', '')
| | found = true |
| local found = false
| | elseif type(options.wrappers) == 'table' then |
| if matchesTitle(options.wrappers, title) then
| | for _,v in pairs(options.wrappers) do |
| found = true
| | if matchesTitle(v, title) then |
| elseif type(options.wrappers) == 'table' then
| | found = true |
| for _,v in pairs(options.wrappers) do
| | break |
| if matchesTitle(v, title) then
| | end |
| found = true
| | end |
| break
| | end |
| end
| |
| end
| |
| end
| |
|
| |
|
| -- We test for false specifically here so that nil (the
| | -- We test for false specifically here so that nil (the default) acts like true. |
| -- default) acts like true.
| | if found or options.frameOnly == false then |
| if found or options.frameOnly == false then
| | pargs = parent.args |
| pargs = parent.args
| | end |
| end
| | if not found or options.parentOnly == false then |
| if not found or options.parentOnly == false then
| | fargs = frame.args |
| fargs = frame.args
| | end |
| end
| | end |
| end
| | else |
| -- When options.wrapper isn't set, check the other options.
| | -- options.wrapper isn't set, so check the other options. |
| else
| | if not options.parentOnly then |
| if not options.parentOnly then
| | fargs = frame.args |
| fargs = frame.args
| | end |
| end
| | if not options.frameOnly then |
| if not options.frameOnly then
| | local parent = frame:getParent() |
| local parent = frame:getParent()
| | pargs = parent and parent.args or nil |
| pargs = parent and parent.args or nil
| | end |
| end
| | end |
| end
| | if options.parentFirst then |
| if options.parentFirst then
| | fargs, pargs = pargs, fargs |
| fargs, pargs = pargs, fargs
| | end |
| end
| | else |
| else
| | luaArgs = frame |
| luaArgs = frame
| | end |
| end
| |
|
| |
|
| -- Set the order of precedence of the argument tables. If the variables are
| | -- Set the order of precedence of the argument tables. If the variables are |
| -- nil, nothing will be added to the table, which is how we avoid clashes
| | -- nil, nothing will be added to the table, which is how we avoid clashes |
| -- between the frame/parent args and the Lua args.
| | -- between the frame/parent args and the Lua args. |
| local argTables = {fargs}
| | local argTables = {fargs} |
| argTables[#argTables + 1] = pargs
| | argTables[#argTables + 1] = pargs |
| argTables[#argTables + 1] = luaArgs
| | argTables[#argTables + 1] = luaArgs |
|
| |
|
| -- Generate the tidyVal function. If it has been specified by the user, we
| | --[[ |
| -- use that; if not, we choose one of four functions depending on the
| | -- Generate the tidyVal function. If it has been specified by the user, we |
| -- options chosen. This is so that we don't have to call the options table
| | -- use that; if not, we choose one of four functions depending on the |
| -- every time the function is called.
| | -- options chosen. This is so that we don't have to call the options table |
| local tidyVal = options.valueFunc
| | -- every time the function is called. |
| if tidyVal then
| | --]] |
| if type(tidyVal) ~= 'function' then
| | local tidyVal = options.valueFunc |
| error(i18n:msg('error-value-func', type(tidyVal)), 2)
| | if tidyVal then |
| end
| | if type(tidyVal) ~= 'function' then |
| elseif options.trim ~= false then
| | error( |
| if options.removeBlanks ~= false then
| | "bad value assigned to option 'valueFunc'" |
| tidyVal = tidyValDefault
| | .. '(function expected, got ' |
| else
| | .. type(tidyVal) |
| tidyVal = tidyValTrimOnly
| | .. ')', |
| end
| | 2 |
| else
| | ) |
| if options.removeBlanks ~= false then
| | end |
| tidyVal = tidyValRemoveBlanksOnly
| | elseif options.trim ~= false then |
| else
| | if options.removeBlanks ~= false then |
| tidyVal = tidyValNoChange
| | tidyVal = tidyValDefault |
| end
| | else |
| end
| | tidyVal = tidyValTrimOnly |
| | end |
| | else |
| | if options.removeBlanks ~= false then |
| | tidyVal = tidyValRemoveBlanksOnly |
| | else |
| | tidyVal = tidyValNoChange |
| | end |
| | end |
|
| |
|
| -- Set up the args, metaArgs and nilArgs tables. args will be the one
| | --[[ |
| -- accessed from functions, and metaArgs will hold the actual arguments. Nil
| | -- Set up the args, metaArgs and nilArgs tables. args will be the one |
| -- arguments are memoized in nilArgs, and the metatable connects all of them
| | -- accessed from functions, and metaArgs will hold the actual arguments. Nil |
| -- together.
| | -- arguments are memoized in nilArgs, and the metatable connects all of them |
| local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}
| | -- together. |
| setmetatable(args, metatable)
| | --]] |
| | local args, metaArgs, nilArgs, metatable = {}, {}, {}, {} |
| | setmetatable(args, metatable) |
|
| |
|
| -- Accepts multiple tables as input and merges their keys and values
| | local function mergeArgs(tables) |
| -- into one table. If a value is already present it is not overwritten;
| | --[[ |
| -- tables listed earlier have precedence. We are also memoizing nil
| | -- Accepts multiple tables as input and merges their keys and values |
| -- values, which can be overwritten if they are 's' (soft).
| | -- into one table. If a value is already present it is not overwritten; |
| local function mergeArgs(tables)
| | -- tables listed earlier have precedence. We are also memoizing nil |
| for _, t in ipairs(tables) do
| | -- values, which can be overwritten if they are 's' (soft). |
| for key, val in pairs(t) do
| | --]] |
| if metaArgs[key] == nil and nilArgs[key] ~= 'h' then
| | for _, t in ipairs(tables) do |
| local tidiedVal = tidyVal(key, val)
| | for key, val in pairs(t) do |
| if tidiedVal == nil then
| | if metaArgs[key] == nil and nilArgs[key] ~= 'h' then |
| nilArgs[key] = 's'
| | local tidiedVal = tidyVal(key, val) |
| else
| | if tidiedVal == nil then |
| metaArgs[key] = tidiedVal
| | nilArgs[key] = 's' |
| end
| | else |
| end
| | metaArgs[key] = tidiedVal |
| end
| | end |
| end
| | end |
| end
| | end |
| | end |
| | end |
|
| |
|
| -- Define metatable behaviour. Arguments are memoized in the metaArgs table,
| | --[[ |
| -- and are only fetched from the argument tables once. Fetching arguments
| | -- Define metatable behaviour. Arguments are memoized in the metaArgs table, |
| -- from the argument tables is the most resource-intensive step in this
| | -- and are only fetched from the argument tables once. Fetching arguments |
| -- module, so we try and avoid it where possible. For this reason, nil
| | -- from the argument tables is the most resource-intensive step in this |
| -- arguments are also memoized, in the nilArgs table. Also, we keep a record
| | -- module, so we try and avoid it where possible. For this reason, nil |
| -- in the metatable of when pairs and ipairs have been called, so we do not
| | -- arguments are also memoized, in the nilArgs table. Also, we keep a record |
| -- run pairs and ipairs on the argument tables more than once. We also do
| | -- in the metatable of when pairs and ipairs have been called, so we do not |
| -- not run ipairs on fargs and pargs if pairs has already been run, as all
| | -- run pairs and ipairs on the argument tables more than once. We also do |
| -- the arguments will already have been copied over.
| | -- not run ipairs on fargs and pargs if pairs has already been run, as all |
| | -- the arguments will already have been copied over. |
| | --]] |
|
| |
|
| -- Fetches an argument when the args table is indexed. First we check
| | metatable.__index = function (t, key) |
| -- to see if the value is memoized, and if not we try and fetch it from
| | --[[ |
| -- the argument tables. When we check memoization, we need to check
| | -- Fetches an argument when the args table is indexed. First we check |
| -- metaArgs before nilArgs, as both can be non-nil at the same time.
| | -- to see if the value is memoized, and if not we try and fetch it from |
| -- If the argument is not present in metaArgs, we also check whether
| | -- the argument tables. When we check memoization, we need to check |
| -- pairs has been run yet. If pairs has already been run, we return nil.
| | -- metaArgs before nilArgs, as both can be non-nil at the same time. |
| -- This is because all the arguments will have already been copied into
| | -- If the argument is not present in metaArgs, we also check whether |
| -- metaArgs by the mergeArgs function, meaning that any other arguments
| | -- pairs has been run yet. If pairs has already been run, we return nil. |
| -- must be nil.
| | -- This is because all the arguments will have already been copied into |
| metatable.__index = function (t, key)
| | -- metaArgs by the mergeArgs function, meaning that any other arguments |
| if type(key) == 'string' then
| | -- must be nil. |
| key = options.translate[key]
| | --]] |
| end
| | if type(key) == 'string' then |
| local val = metaArgs[key]
| | key = options.translate[key] |
| if val ~= nil then
| | end |
| return val
| | local val = metaArgs[key] |
| elseif metatable.donePairs or nilArgs[key] then
| | if val ~= nil then |
| return nil
| | return val |
| end
| | elseif metatable.donePairs or nilArgs[key] then |
| for _, argTable in ipairs(argTables) do
| | return nil |
| local argTableVal = tidyVal(key, argTable[key])
| | end |
| if argTableVal ~= nil then
| | for _, argTable in ipairs(argTables) do |
| metaArgs[key] = argTableVal
| | local argTableVal = tidyVal(key, argTable[key]) |
| return argTableVal
| | if argTableVal ~= nil then |
| end
| | metaArgs[key] = argTableVal |
| end
| | return argTableVal |
| nilArgs[key] = 'h'
| | end |
| return nil
| | end |
| end
| | nilArgs[key] = 'h' |
| | return nil |
| | end |
|
| |
|
| metatable.__newindex = function (t, key, val)
| | metatable.__newindex = function (t, key, val) |
| -- This function is called when a module tries to add a new
| | -- This function is called when a module tries to add a new value to the |
| -- value to the args table, or tries to change an existing
| | -- args table, or tries to change an existing value. |
| -- value.
| | if type(key) == 'string' then |
| if type(key) == 'string' then
| | key = options.translate[key] |
| key = options.translate[key]
| | end |
| end
| | if options.readOnly then |
| if options.readOnly then
| | error( |
| error(i18n:msg('error-write-permission', tostring(key)), 2)
| | 'could not write to argument table key "' |
| elseif options.noOverwrite and args[key] ~= nil then
| | .. tostring(key) |
| error(i18n:msg('error-overwrite-permission', tostring(key)), 2)
| | .. '"; the table is read-only', |
| elseif val == nil then
| | 2 |
| -- If the argument is to be overwritten with nil, we need to erase
| | ) |
| -- the value in metaArgs, so that __index, __pairs and __ipairs do
| | elseif options.noOverwrite and args[key] ~= nil then |
| -- not use a previous existing value, if present; and we also need
| | error( |
| -- to memoize the nil in nilArgs, so that the value isn't looked
| | 'could not write to argument table key "' |
| -- up in the argument tables if it is accessed again.
| | .. tostring(key) |
| metaArgs[key] = nil
| | .. '"; overwriting existing arguments is not permitted', |
| nilArgs[key] = 'h'
| | 2 |
| else
| | ) |
| metaArgs[key] = val
| | elseif val == nil then |
| end
| | --[[ |
| end
| | -- If the argument is to be overwritten with nil, we need to erase |
| | -- the value in metaArgs, so that __index, __pairs and __ipairs do |
| | -- not use a previous existing value, if present; and we also need |
| | -- to memoize the nil in nilArgs, so that the value isn't looked |
| | -- up in the argument tables if it is accessed again. |
| | --]] |
| | metaArgs[key] = nil |
| | nilArgs[key] = 'h' |
| | else |
| | metaArgs[key] = val |
| | end |
| | end |
|
| |
|
| local function translatenext(invariant)
| | local function translatenext(invariant) |
| local k, v = next(invariant.t, invariant.k)
| | local k, v = next(invariant.t, invariant.k) |
| invariant.k = k
| | invariant.k = k |
| if k == nil then
| | if k == nil then |
| return nil
| | return nil |
| elseif type(k) ~= 'string' or not options.backtranslate then
| | elseif type(k) ~= 'string' or not options.backtranslate then |
| return k, v
| | return k, v |
| else
| | else |
| local backtranslate = options.backtranslate[k]
| | local backtranslate = options.backtranslate[k] |
| if backtranslate == nil then
| | if backtranslate == nil then |
| -- Skip this one. This is a tail call, so this
| | -- Skip this one. This is a tail call, so this won't cause stack overflow |
| -- won't cause stack overflow.
| | return translatenext(invariant) |
| return translatenext(invariant)
| | else |
| else
| | return backtranslate, v |
| return backtranslate, v
| | end |
| end
| | end |
| end
| | end |
| end
| |
|
| |
|
| -- This metamethod is called when pairs is run on the args table.
| | metatable.__pairs = function () |
| metatable.__pairs = function ()
| | -- Called when pairs is run on the args table. |
| if not metatable.donePairs then
| | if not metatable.donePairs then |
| mergeArgs(argTables)
| | mergeArgs(argTables) |
| metatable.donePairs = true
| | metatable.donePairs = true |
| end
| | end |
| return translatenext, { t = metaArgs }
| | return translatenext, { t = metaArgs } |
| end
| | end |
|
| |
|
| -- This custom `ipairs`-style iterator uses our __index metamethod.
| | local function inext(t, i) |
| local function inext(t, i)
| | -- This uses our __index metamethod |
| local v = t[i + 1]
| | local v = t[i + 1] |
| if v ~= nil then
| | if v ~= nil then |
| return i + 1, v
| | return i + 1, v |
| end
| | end |
| end
| | end |
|
| |
|
| -- This metamethod is called when ipairs is run on the args table.
| | metatable.__ipairs = function (t) |
| metatable.__ipairs = function (t)
| | -- Called when ipairs is run on the args table. |
| return inext, t, 0
| | return inext, t, 0 |
| end
| | end |
|
| |
|
| return args
| | return args |
| end | | end |
|
| |
|
| return arguments | | return arguments |