Help:Lua for beginners
Overview
[edit]Lua is a lightweight scripting language safely available on Wikipedia via Scribunto. Its purpose is to allow you to process the data which is available on Wikipedia content pages to allow various sorts of customized display of information.
It runs in a sandboxed environment to protect the stability of Wikipedia while enabling powerful {{template}} and module: functionality.
The most important help file is the MediaWiki Scribunto Lua reference manual, which provides a concise summary of the language and standard library calls as implemented on MediaWiki.
The standard Lua reference manual is well written and comprehensive, but it can be confusing for beginners because some standard features don't work on wiki pages. You cannot call print("Hello, World!") in a module, but you can preview your User page showing the string "Hello World!" generated by the Lua Module:Example. Think of your Lua template as an HTML generator.
Generating "Hello World!"
[edit]You do not need to install or save anything.
- Edit your User page.
- Paste the following call at the beginning of a new line on your User page:
{{#invoke:Example|hello}}
- Click
Preview. The call of the functionhelloin the Module:Example is replaced with the following:
Hello World!
- Click
Cancel. When prompted, clickLeave. - Your User page remains unmodified.
Debug console: Start learning Lua
[edit]The Lua Scribunto Debug Console is a safe, interactive tool for learning Lua in Wikipedia.
- Click
View sourceon the Module:Yesno page. - Scroll to the bottom of the page to the
Debug consolesection. - Paste the comment and the call into the gray input box (above the
Clearbutton).
--[[Your very first Lua Hello World-like program]]-- print("Welcome to " .. _VERSION .. "!") -- Concatenate the Lua version with `..`
- Press ↵ Enter once to execute the two Lua commands.
- The result will appear below the Lua
printcommand line:
Welcome to Lua 5.1!
- Press
Up arrow(↑) to cycle through previous Lua commands. - Optionally, click
Clearto clear only theDebug Console; the Lua editor above will remain unmodified.
- HTML generator in the Debug console
Call the function hello in Module:Example:
local modExample = require('Module:Example') -- load the module in Debug console local frame = mw.getCurrentFrame(); print(modExample.hello(frame)) -- display "Hello World!"
Hello World!
- Identity function
local yesno = require('Module:Yesno') -- #if yesno then returns its input parameter print(mw.getCurrentFrame():callParserFunction("#if", yesno("Yes"), "Yes", 'no'))
Yes
- Close the protected
Module:Yesno that remains unmodified.
Issues with the current implementation
[edit]Besides the lack of print() in a module, there are other features missing – see Differences from standard Lua for a complete list.
At the present time, it is advisable to use mw.ustring functions instead of string, because the latter sometimes fails with Unicode characters.
local lang = mw.getCurrentFrame():callParserFunction("#language", "ja") -- Japanese local lenUnicode, lenByte, lenSharp = mw.ustring.len(lang), string.len(lang), #lang print(("lang: %s Mw.ustring.len = %d String.len = %d Sharp = %d"):format(lang, lenUnicode, lenByte, lenSharp))
Debug console supports Unicode:
lang: 日本語 Mw.ustring.len = 3 String.len = 9 Sharp = 9
Input
[edit]The programs are run only when the page is "parsed" (when it or a page it incorporates is changed or previewed), not every time you view the output. Therefore there can be no convenient Lua module that allows you to type in a Fahrenheit temperature in an input box and get back the corresponding Celsius temperature when you press a button, or allows you to click on a segment of a Mandelbrot set visualization on a page to expand it as often as you like. There has to be an actual Wiki page (or at least a page you have submitted for preview) containing the input data.
local function toCelsius(tempFahrenheit) return (tempFahrenheit - 32) * 5 / 9 end print(("toCelsius(32°F) = %s°C. toCelsius(68°F) = %d°C"):format(toCelsius(32), toCelsius(68)))
toCelsius(32°F) = 0°C. toCelsius(68°F) = 20°C
However, it is possible to use library functions like mw.title.getCurrentTitle() to get the title of the current module.
print(mw.title.getCurrentTitle())
Module:Example
You cannot, however, import data from files, not even .svg files which contain XML text data.
Calling a Lua module
[edit]Lua calls look much like templates, and consist of a small block of text.
In your User page:
{{ConvertNumeric|decToHex|73}}
Click Preview or click Edit this section to see the call then Cancel:
49
This text calls the Lua script itself, which is housed in the Module: namespace. The effect of this call is to send the information within the #invoke block to the Lua module, and to replace everything within the brackets with a piece of text that it sends back in return. (Literally, in the "return" statement)
Note that the first "parameter", in this case decToHex, is actually a function called within the Lua module. This field must always be included in any #invoke. To those unfamiliar with modules, especially Wikipedia template coders who expect anything after | to be a parameter, the need for this extra field is surprising, especially if all uses of the module depend on its presence.
In Debug console of Module:Yesno:
local convNum = require('Module:ConvertNumeric') local frame=mw.getCurrentFrame(); frame.args[1] = "73" -- number always as string print(("0x%s = %s = %d"):format(convNum.decToHex(frame), string.format("0x%X", 73), 0x49))
0x49 = 0x49 = 73
Documenting a Lua module
[edit]When documenting your module, it is useful to include an explicit usage instruction using Template:Module link expanded:
{{mlx|ConvertNumeric|decToHex|73}}
Hyperlink to the module:
{{#invoke:ConvertNumeric|decToHex|73}}
For many existing modules, an example #invoke of the script (and little else) is provided on the Module talk: page. It is convenient for authors to be able to flip quickly to the talk tab to look at the effects of their changes, but you should never transclude the talk page as a template - people might actually talk on it! Alternatively, the module page can show documentation from a separate /doc-page (as Module:WikidataIB does).
Another example: Using Lua call to perform a single Lua instruction
[edit]As a beginner, or in casual talk page conversation, you might only have one little calculation you want to use Lua for but don't want to write a full module.
You might find Module:Lua call convenient for this.
For example, count up the length of a string about Did you know (DYK).
In your user page:
{{#invoke:LuaCall|main|strText=count the length of your DYK hook with Lua|string.len(strText)}}
42
You can test how a greedy Lua pattern works: regular expression .* matches zero or more. .+ matches one or more.
In your user page:
{{#invoke:LuaCall|main|strText=bbbbbbbbbba|regex=bb(.*)b(.+)bba|string.match(strText,regex)}}
Module:LuaCall returns the first match ignoring other results:
bbbb
In these specific examples, however, Module:String could do both of these tasks.
In Debug console of Module:Yesno:
--[[Did You Know (DYK)]]-- local strText = "count the length of your DYK hook with Lua"; print(string.len(strText)) strText = "bbbbbbbbbba"; local regex = "bb(.*)b(.+)bba" -- greedy pattern as much as possible local table = {string.match(strText, regex)}; print(table[1]) -- returns the first match
42 bbbb
- Unicode
mw.ustring.gsub
In Japanese, plurality is not marked by a single ending on the noun but could be before the particle no.
In your User page: remove plural "れら" between first "そ" and particle no "の" in "それらの本がある。" ("There are those books.")
{{#invoke:Lua call|main|subjectStr=それらの本がある。|pattern=れら|replace=|mw.ustring.gsub(subjectStr,pattern,replace)|lua}}
Click Preview. Singular: "There is that book." 本 = book. Click Edit this section to see the call then Cancel:
その本がある。
In Debug console of Module:Yesno:
--[[According to the number of books, select singular or plural]]-- local pluralMarker = "れら"; local particleNo = "の" local bookSingular = "そ".. particleNo.."本がある。"; -- There is that book. local booksPlural = "そ"..pluralMarker..particleNo.."本がある。"; -- There are those books. print("There are those books:") -- plural for 42 books print(mw.getCurrentFrame():callParserFunction("plural", {'42', bookSingular, booksPlural})) --[[Replace the plural marker with the empty string]]-- -- Generic frame Builder simulates {{TemplateTitle | parentArgs}} local function buildFrame(templateTitle, parentArgs, moduleName, childArgs) local root = mw.getCurrentFrame() local parent = root:newChild{title=templateTitle, args=parentArgs or {}} -- Child frame: simulates {{#invoke:moduleName |entryPoint| childArgs}} return parent:newChild{title=moduleName, args=childArgs or {}} end local globalSub = "mw.ustring.gsub(subjectStr,pattern,replace)" -- no space separator local prm = {subjectStr=booksPlural, pattern=pluralMarker, replace="", globalSub, "lua"} local moduleName = 'Module:Lua call' local frame = buildFrame(moduleName, prm, moduleName) print("There is that book:\n"..require(moduleName).main(frame)) -- singular
There are those books: それらの本がある。 There is that book: その本がある。
The script at Module:Lua call has been written to accept any set of named parameters somename=value, for each one storing the string value in the variable with the name somename, and then allowing you to use these variables as parameters for any function available in Lua. The script then returns only the first value returned by the function (Lua functions can return multiple values, but in this case, only the first is returned from the module).
Lua program structure: Output
[edit]The most fundamental part of a Wikipedia Lua program is a return statement which carries its output back to the page that had the #invoke. You can have a Lua function that runs without error even though it doesn't contain a return statement, but on Wikipedia it is pointless, as Lua programs cannot generally have side effects on Wikipedia.
The module itself must return a Lua table of values. A Lua table is expressed as a list of values separated by commas, within curly braces. When the module is called by #invoke, the function it names (the first argument after |) is looked for in that table. That function, in turn, is expected to return something that can be represented as a string.
Typically we use the overall form for Module:Example:
local p = {} -- Defines the table package as an empty table, but *not* nil.
function p.hello(frame) -- Define the function p.hello. The input parameter frame is not used
return "Hello World!" -- Returns the string result of the function.
end -- Ends the function
return p -- This returns the table package
Note that function p.hello(frame) is equivalent to anonymous p.hello = function(frame) or p["hello"] = function(frame).
The function is just another type of value, retrieved with the key "hello" from table p.
If you want to allow users to invoke the same module with {{#invoke:module-name|main}} instead of {{#invoke:Example|hello}}, you can write:
p.main = p.hello -- to copy the reference to this function to a new key in the table.
You can even write p[""] = p.hello, which causes {{#invoke:module-name|}} to produce the same output as {{#invoke:Example|hello}}.
Learning to think of functions as a data type becomes very important later on for working with library functions like mw.ustring.gsub, and constructing iterator functions.
- gmatch-based advanced minimal module iterator of vowels
The Module:MatchVowel is actually a complete Lua module (though a very strange one):
--[[MatchVowel returns a table containing the iterator function of vowels]]-- return { mw.ustring.gmatch( "Hello World!", "([aeiou])" ) } -- returns a table with one value: the iterator
It returns the iterator function returned by mw.ustring.gmatch as the one and only element in an array.
If Module:MatchVowel was saved in its own wiki page, when executed in another wiki page:
{{#invoke:MatchVowel|1}}
The call yields the vowel from "Hello World!":
e
In Debug console of Module:Yesno:
--[[iterator function that yields each vowel from the string "Hello World!"]]-- local tabMatchVowel = (function() return {mw.ustring.gmatch("Hello World!", "([aeiou])")} end)() local itv = tabMatchVowel[1] -- iterator function from mw.ustring.gmatch as the sole element of the table local vowel; repeat vowel = itv(); if vowel then print(vowel) end until not (vowel) -- loop for each vowel
e o o
Lua program structure: Input
[edit]The frame parameter above (which is pretty much always given this name in Wikipedia Lua modules) receives another table, which is passed from the page that makes the call to the Lua module. It contains a surprising amount of stuff, of which just a few things concern the novice.
Arguments
[edit]frame.args contains another table, namely, all the content sent by the user within the #invoke brackets except the first argument which states the name of the function to be executed.
In your User page: about Module:ConvertNumeric
{{#invoke:ConvertNumeric |numeral_to_english |57000 |round=on |plural=on}}
Click Preview or click Edit this section to see the call then Cancel:
sixty thousand
There are two types of parameters: numeric (positional) keys and named keys.
- Unnamed parameters come out with numbers as keys:
frame.args[1].
The string"57000"is the content offrame.args[1]which is the same asframe["args"][1]
but not the same asframe.args["1"]orframe["args"]["1"]. - Named parameters come out with the parameter names (strings) as keys:
frame.args["round"],frame.args["plural"].
In Debug console of Module:Yesno:
--[[Convert a number to letter in English]]-- local convNum = require('Module:ConvertNumeric') local frame=mw.getCurrentFrame(); frame.args = {"57000", round="on", plural="on"} print(convNum.numeral_to_english(frame) .. " is rounded from 57,000.") local debugLog = {} -- empty report table for key, val in pairs(frame.args) do -- for each arg debugLog[#debugLog + 1] = ("args[%s] = %s"):format(key, val) end -- ^length. Adding report is faster than string-based: debugLog = debugLog .. string print(table.concat(debugLog, ". ")) -- Join with separator mw.logObject(frame.args) -- Scribunto tool to display table
sixty thousand is rounded from 57,000. args[1] = 57000. args[plural] = on. args[round] = on table#1 { "57000", ["plural"] = "on", ["round"] = "on", }
Parent frame
[edit]Within frame there is a parent frame, referring to the page that called the page that gives the script, and you can pull out arguments from that also. Just write:
local function buildFrame(templateTitle, parentArgs, moduleName, childArgs) local root = mw.getCurrentFrame() local parent = root:newChild{title=templateTitle, args=parentArgs or {}} -- Child frame: simulates {{#invoke:moduleName |entryPoint| childArgs}} return parent:newChild{title=moduleName, args=childArgs or {}} end local plurMark = "are those books"; local booksPlural = "There "..plurMark.."." local globalSub = "mw.ustring.gsub(subjectStr,pattern,replace)" -- no space separator local singMark = "is that book" local prm = {subjectStr=booksPlural, pattern=plurMark, replace=singMark, globalSub, "lua"} local moduleName = 'Module:Lua call' local frame = buildFrame(moduleName, prm, moduleName) print(require(moduleName).main(frame)) -- singular local parent = frame:getParent() or nil mw.logObject(parent.args)
parent.args will contain those arguments.
There is that book. table#1 { metatable = table#2 "mw.ustring.gsub(subjectStr,pattern,replace)", "lua", ["pattern"] = "are those books", ["replace"] = "is that book", ["subjectStr"] = "There are those books.", }
It is popular in Lua to use the synonymous statement parent=frame:getParent(), cancelling the need to write frame twice.
Note the colon (:) instead of the dot (.).
parent = frame:getParent() means exactly the same as parent = frame.getParent(frame).
For novices this can be confusing, and it is important to be aware of this idiom.
If you use it in the wrong way, though, the script errors are pretty good at pointing out that this was the mistake.
Basic debugging
[edit]Debugging can start as soon as you write programs, and can be done simply with string concatenation.
Just set up a variable with some recognizable name like debugLog in your main function p.main(frame) with a statement like:
local debugLog = "" -- empty report string
This initial "" definition helps because otherwise it will be nil and concatenating a string to nil gets you an error.
Now whenever you have a variable you'd like to test, say myVar, just write:
local myVar = 42; local output = "expected result" debugLog = debugLog .. " myVar = " .. tostring(myVar) -- append to the report
At the end of your function (but not the module), complete:
return output .. debugLog -- Returns the expected result and the report
The tostring(myVar) is a function to ensure myVar is interpreted as a string. For a table, it will display as "table". For nil, "nil" rather than as Script error.
Parser function
[edit]local frame = mw.getCurrentFrame()
local candidates = {
{"#language", "ko"}, -- Set the output language to (ko)rean (the #language parser function)
{"#tag", {'isbn', '978-4-87311-471-2'}}, -- Generate an <isbn> tag: passing multiple args to the #tag parser function)
{"plural", {'42', 'book', 'books'}}, -- Switch sentences based on number (example of the plural parser function)
{"canonicalurl", tostring(mw.title.getCurrentTitle())}, -- Return the URL of the page with canonicalurl parser function
}
local function args_to_string(obj)
if type(obj) == "table" then return table.concat(obj, " ") else return tostring(obj or "") end
end
for _, pair in ipairs(candidates) do -- _ = index (ignored), pair = value; ipairs iterates numeric keys in order
local fn, arg = pair[1], pair[2] -- The parser function name to call and its argument (string or table of args)
local ok, res
if type(arg) == "table" then
-- If the argument is a table, unpack it and pass as multiple arguments
ok, res = pcall(function() return frame:callParserFunction(fn, unpack(arg)) end)
else
-- If the argument is a single value (string), pass it as-is
ok, res = pcall(function() return frame:callParserFunction(fn, arg or "") end) -- Safe call (pcall)
end
local out = ok and tostring(res) or ("<error>") -- Convert to string on success, "<error>" on failure
-- Join argument tables for readability. Output is for the Lua console (adjust to return for wiki content).
print(string.format('%s(%q) -> %s', fn, args_to_string(arg), out))
end
#language("ko") -> 한국어
#tag("isbn 978-4-87311-471-2") -> <isbn>978-4-87311-471-2</isbn>
plural("42 book books") -> books
canonicalurl("Module:Example") -> https://en.wikipedia.org/wiki/Module:Example
Cite book
[edit]In your User page, paste the following Template:Cite book:
{{cite book |last=Solomon |first=Maynard |title=Mozart: a life |publisher=HarperCollins |publication-place=New York |year=1995 |isbn=978-0-06-019046-0 |url=https://archive.org/details/mozartlife00solo |oclc=31435799}}
Click Preview:
Solomon, Maynard (1995). Mozart: a life. New York: HarperCollins. ISBN 978-0-06-019046-0. OCLC 31435799.
In Debug console of Module:Yesno:
local function buildFrame(templateTitle, parentArgs, moduleName, childArgs) local root = mw.getCurrentFrame() local parent = root:newChild{title=templateTitle, args=parentArgs or {}} return parent:newChild{title=moduleName, args=childArgs or {}} end local prm = {last="Solomon", first="Maynard", title="Mozart: a life", publisher="HarperCollins", ["publication-place"]="New York", year="1995", isbn="978-0-06-019046-0", language="en", url="https://archive.org/details/mozartlife00solo", oclc="31435799" } local moduleName = "Module:citation/CS1"; local childArg = {CitationClass = "book"} local frame = buildFrame("Template:Cite book", prm, moduleName, childArg) print(require(moduleName).citation(frame))
The generated HTML starts with:
<DEL>'"`UNIQ--templatestyles-00000000-QINU`"'<DEL><cite id="CITEREFSolomon1995" class="citation book cs1">Solomon, Maynard (1995). [https://archive.org/details/mozartlife00solo ''Mozart: a life'']. New York: HarperCollins. [[ISBN (identifier)|ISBN]] [[Special:BookSources/978-0-06-019046-0|<bdi>978-0-06-019046-0</bdi>]]. [[OCLC (identifier)|OCLC]] [https://search.worldcat.org/oclc/31435799 31435799].</cite><span title="ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&rft.genre=book&rft.btitle=Mozart%3A+a+life&rft.place=New+York&rft.pub=HarperCollins&rft.date=1995&rft_id=info%3Aoclcnum%2F31435799&rft.isbn=978-0-06-019046-0&rft.aulast=Solomon&rft.aufirst=Maynard&rft_id=https%3A%2F%2Farchive.org%2Fdetails%2Fmozartlife00solo&rfr_id=info%3Asid%2Fen.wikipedia.org%3AModule%3AYesno" class="Z3988"></span>
- Hidden code
"UNIQ--templatestyles.*-QINU"between non‑printing 0x7F DEL markers stripped out with"<DEL>". <cite id="CITEREFSolomon1995"for Template:Sfn.
Format
[edit]The WP:Lua style guide gives some basic formatting suggestions expected by the JavaScript module editor, such as using four-space indentations and keeping if, then, else, end at the same level of indentation.
Comments to the end of a line are marked by -- . Use them. Many modules for Wikipedia have a straightforward, linear design, but that doesn't mean it won't help to have your sections clearly labelled when you go back to the code for the hundredth time. The Lua style guide gives additional recommendations for using functions to keep your work more organized.
Errors
[edit]Lua errors appear as red "Script error" messages. If Javascript is enabled, the red script error message is a link which usually allows you to follow it back to the line in the module where the error occurred. There are some exceptions, for example "Module not found", if the name of the module itself is mistyped, or "The function you specified did not exist" if the function name given is invalid.
Recurrent bugs
[edit]Some bugs you might want to keep in mind:
- Missing operator for concatenation
local debugLog = "" -- empty report string debugLog = debugLog "missing double dot" -- append to the report
The following means you forgot the .. between a string and a variable somewhere in a mess of stuff you're concatenating.
Lua error in console input at line 8: attempt to call local 'debugLog' (a string value).
- String expected, got function
local debugLog = "" -- empty report string local res = mw.ustring.gmatch("Hello World!", "([aeiouAEIOU])") -- iterator of vowels debugLog = debugLog..res -- populate the report with only strings
Some important things like mw.ustring.gmatch actually return functions, not strings - see Functions below.
Lua error in console input at line 9: attempt to concatenate local 'res' (a function value).
- Variable ignores assignment
A variable ignores all your efforts to assign stuff to it:
local debugLog = "top level" -- initial value do local debugLog = "nested level" -- set the new value print("Inside do: "..debugLog) end print("Outside do: "..debugLog) -- get the old value
You may have inadvertently written two local statements - the one sets the value of the variable within a limited region, and when the program leaves that region, you're back to the old value.
Inside do: nested level Outside do: top level
A numbered table entry ignores all your efforts to assign to it:
local table = {}; local arg = "50"; local valPrm = tonumber(arg) table[arg] = "index as string" table[valPrm] = "index as number" print(table[arg] == table[valPrm])
This is because table["50"] is not table[50].
Typically you have processed a parameter (which you may have received from the #invoke as a string) with string functions in one place,
but performed numeric operations in another, leaving you with two different types of variable to use for an index.
false
niland common pitfalls
There are all sorts of things you can't do with a local variable set to nil, such as: local x = nil, like:
- Assign
x.somefield - Get value at index
x[idx] - Concatenate
x .. "Cannot concatenate with nil" - Evaluate
table[x]
Initialize such variables with: local x={}; local table = {}
Often "global" is mentioned in these errors because you didn't have a local statement for the nil variable.
- Cannot invoke module
- no such module. You called
#invoke:moduleNameUnknownthat didn't exist or you wrongly kept the prefixModule:writing#invoke:Module:moduleName. - the function specified did not exist. You called
#invoke:moduleName|functionUnknown, but the field after the name of the module is wrong.
Often this field expects a standard name like "main", and you've forgotten it and gone straight to the first data parameter.
If you're unsure of the function name, check the module documentation, or look for what function(s) in the code accept a "frame" parameter.
- Graphics overflow issue
Some graphics you're trying to display are heading off to the hills: actually a HTML error.
You didn't close one </div>, so all the top: and left: styles keep adding up.
Understanding tables and related concepts
[edit]- An expression list is a set of values separated by commas. The values can be strings, numbers, tables, functions, etc.
- A sequence is a set of entries with indices from 1 to N, where N is a positive integer. They can be created by placing brackets around an expression list.
For example:
local seq = {1, "quotation", mw.ustring.gmatch("abca","a"), {2,3,4}} mw.logObject(seq)
table#1 { 1, "quotation", function#1, table#2 { 2, 3, 4, }, }
seq[1]= 1seq[2]="quotation"seq[3]is thefunctionreturned bygmatch()seq[4]is the table{2,3,4}.
An expression list can also be recovered from a table using unpack():
local seq = {1, "quotation", mw.ustring.gmatch("abca","a"), {2,3,4}} local firstNbr, secondStr, thirdFct = unpack(seq) print(("firstNbr = %d. secondStr = \"%s\". thirdFct = %s"):format(firstNbr, secondStr, type(thirdFct)))
firstNbr = 1. secondStr = "quotation". thirdFct = function
This will set:
firstNbr= 1secondStr="quotation"thirdFctas thefunctionreturned bygmatch(){2,3,4}will be discarded in this case.
- A table is a sequence, optionally supplemented by named keys:
digit["two"]="2". Several table functions liketable.concatwill only work with the numbered values and ignore named keys. - The metatable offers a large, optional set of methods for altering table behavior. For example, you can define a table to be callable like a function.
Initializing a table
[edit]It is often useful to create a whole table at once in a statement. There are many equivalent ways to do this, but the shortcuts don't work for every kind of value. To begin with, the most general way is to assign each key and value explicitly:
a = {[0]='zero', [1]='one', ['1']='string for one'}
If sequence keys (positive integers) are given in order, only the values need to be given, so the following will assign 'one' to a[1]:
a = {[0]='zero', 'one', ['1']='string for one'}
If a key has only letters, digits, and underscores, and begins with a non-digit, the brackets and quotation marks can be omitted:
a = {a='one', b='two'}
This is identical to a = {["a"]='one', ["b"]='two'}.
However, this will fail for keys that begin with a digit: hex = {7f = 127} will produce an error; use hex = {['7f'] = 127} instead.
Note that when given within brackets, or to the right of the equal sign, quotation marks are needed, or else string values will be taken as variables:
a = {[b] = c}
assigns the value of variable c to the key contained in variable b.
Functions
[edit]- Functions can return any kind of value — including a function. This is a powerful feature that can readily confuse the beginner. If you set
a=mw.ustring.gmatch(text, "(.)"), the result assigned toawill be a function, not a string character! However, assigningb=a()by calling the function stored inawill return the first match (a string). Every time you setb=a()after that you'll get another match (string) intob, until you run out of matches and getnil. Many iterator functions act this way. - You can keep separate counts for iterator functions by using different variables. For example, if you set
q=mw.ustring.gmatch(text, "(x.)")in the same module, you can pull characters from the same piece of text (text) by evaluatingd=q()without losing your place ina(). - Tail calls offer substantial benefits in performance for those who master the language.
- Function names are often of the form
p.myFunctionName, where p is the table from thereturn pat the bottom of your program. The reason for this is that you can only access functions that are entries in this table from the original#invokestatement. Functions for local use within the program can have any name.
Understanding patterns
[edit]Note: Lua patterns are not regular expressions in the traditional POSIX sense, and they are not even a subset of regular expressions. But they share many constructs with regular expressions (more below).
Lua patterns are used to define, find and handle a pattern in a string. It can do the common search and replace action in a text, but it has more options that doing plain text only. For example, in one go it can change the errors 'New yorker', 'New-Yorker', and 'NewYorker' into 'New Yorker'.
- To begin with, a pattern works like a plain string so long as it doesn't contain the special characters
^ $ () % . [] * + - ?
- Square brackets
[ ]are used to match one single character in the string from a list of choices.[abc]matches the letters a, b, or c. With^right after[they indicate "anything but":[^abc]= not a, b, or c. Inside brackets and when not the first character, a minus-indicates a range:[a-z]matches one single character from a, b, c, …, z.
- Period
.matches any character.
- Percent
%indicates a large set (class) of possible character matches when it is followed by a letter. See [1] for a full list. When followed by punctuation (whether a special character above or not) the%is removed and the punctuation is taken as a literal character;%%= literal %. Special classes include a balanced class%bxyand%f[set]; see the link above for more.
- Parentheses
( )indicate captures. The captures can be accessed later in the search string or in the string.gsub replacement string as%1to%9, and are returned by string.match as an expression list of results.
- The qualifiers
? - * +specify repetitions of a single character (not a longer string).
?means 0 or 1 repetitions:a?matches "a" or "".-means 0 or more repetitions, choosing as few as possible to achieve a match ("non-greedy"). For examplestring.match("bbbb", "(.-)")yields "", which is less than useful because there is nothing to root the ends of the expression and prevent it from matching zero characters.*means 0 or more repetitions, choosing as many as possible ("greedy"). For examplestring.match("bbbb", ".*")yields bbbb.+means 1 or more repetitions, choosing as many as possible ("greedy").
Note that the greediness of the leftmost qualifier rules over all others when there is a choice: (.*)b(.*) when matched on "bbb" will return "bb", "", while a(.-)b(.-)a when matched on "abbba" will return "", "bb".
^and$indicate the beginning and end of the string if they occur in the appropriate place in the pattern. Otherwise they are literal characters.^is not used in thestring.gmatchfunction.
The reference manual for Lua patterns is at mediawiki.org.
Note on Lua patterns versus regular expressions
[edit]Lua patterns are loosely based on regular expressions (sometimes shortened to regex or regexp). Lua patterns deliberately lack the most complex regular expression constructs (to avoid bloating the Lua code base), where many other computer languages or libraries use a more complete set. Lua patterns are not even a subset of regular expressions, as there are also discrepancies, like Lua using the escape character % instead of \,, and additions, like Lua providing - as a non-greedy version of *.
Here is a list of some of the things that Lua patterns lack compared to regular expressions:
- You cannot search for alternations between anything else than single characters (you cannot say
(his|her)to choose betweenhisandher, you can only say[abc]to choose between single charactersa,b, orc). - You cannot look for multiples of multi-letter constructs such as
(choo-)*chooto matchchoo,choo-chooorchoo-choo-choo. There is no way to do this with Lua patterns. - You cannot specify the minimum and maximum number of repetitions like
[0-9]{3,5}(to match 3 to 5 digits); in Lua you would say%d%d%d%d?%d?instead in this case.
There are Lua libraries that offer more powerful options,[2] including regular expressions, but the support on Wikipedia is pretty basic.
Wikipedia help for regular expressions (which Lua, as mentioned, does not support) is at Wikipedia:AutoWikiBrowser/Regular expression.