Rsw:Help:Editing/Lua guide: Difference between revisions

From Illerai
Jump to navigation Jump to search
Created page with "{{construction}} {{SE guide}} {{ToC|right}} '''Lua''' is a scripting language which is able to be used on the wiki. It allows more complex templates to be added than normal wikitext, and is usually easier to read than wikitext templates. Lua is implemented via an extension called Scribunto. ==Important== The primary lua reference guide for this is [https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual Extension:Scribunto/Lua reference manual on MediaW..."
 
Replaced content with "==Important== The primary lua reference guide for this is [https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual Extension:Scribunto/Lua reference manual on MediaWiki] which gives a short description of how to use every function in the standard libraries. While this guide will cover some of this, it will focus more on how to use lua as a whole rather than explaining the use of every available function. For some more explanation on the standard libra..."
Tag: Replaced
 
Line 1: Line 1:
{{construction}}
{{SE guide}}
{{ToC|right}}
'''Lua''' is a scripting language which is able to be used on the wiki. It allows more complex templates to be added than normal wikitext, and is usually easier to read than wikitext templates. Lua is implemented via an extension called Scribunto.
==Important==
==Important==
The primary lua reference guide for this is [https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual Extension:Scribunto/Lua reference manual on MediaWiki] which gives a short description of how to use every function in the standard libraries. While this guide will cover some of this, it will focus more on how to use lua as a whole rather than explaining the use of every available function. For some more explanation on the standard library functions see [[RuneScape:Lua/Library functions]].
The primary lua reference guide for this is [https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual Extension:Scribunto/Lua reference manual on MediaWiki] which gives a short description of how to use every function in the standard libraries. While this guide will cover some of this, it will focus more on how to use lua as a whole rather than explaining the use of every available function. For some more explanation on the standard library functions see [[RuneScape:Lua/Library functions]].
===Editor===
----
Since Scribunto uses the [https://ace.c9.io/ ace editor] when editing lua modules, it offers some features to help you edit efficiently. The most notetable of those are the autocomplete function, line up or down movement/duplication, move to/select next/previous occurrence, toggling comments on or off over multiple lines at once and multiline editing. The autocomplete window can be opened by pressing {{Key press|ctrl|space}}, one can then scroll up or down using the mouse or arrow keys and select the desired word or snippet by clicking with the mouse or using the {{Key press|tab}} key. Multiline editing can be achieved by holding {{Key press|ctrl|alt}} and clicking in multiple locations or dragging the mouse. It also offers a range of keybinds which are listed below.
====Keybinds====
----
=====Line operations=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|ctrl|/}}
| Toggle comment of the current line/selected lines.
|-
| {{Key press|ctrl|shift|/}}
| Toggle block comment. (Doens't work very well in lua)
|-
| {{Key press|ctrl|d}}
| Remove current line/selected lines.
|-
| {{Key press|alt|shift|🡑}}; {{Key press|alt|shift|🡓}}
| Copy line/selected lines up or down.
|-
| {{Key press|alt|🡑}}; {{Key press|alt|🡓}}
| Moves current line or selected lines up or down.
|-
| {{Key press|alt|backspace}}
| Remove to line start.
|-
| {{Key press|alt|delete}}
| Remove to line end.
|-
| {{Key press|ctrl|backspace}}
| Remove untill left word boundary.
|-
| {{Key press|ctrl|delete}}
| Remove untill right word boundary.
|-
| {{Key press|ctrl|shift|d}}
| Duplicate line/selection.
|-
| {{Key press|ctrl|alt|s}}
| Sort selected lines.
|}
=====Selection=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|ctrl|a}}
| Select all.
|-
| {{Key press|shift|🡐}}; {{Key press|shift|🡒}}
| Expand selection to the left/right.
|-
| {{Key press|ctrl|shift|🡐}}; {{Key press|ctrl|shift|🡒}}
| Expand selection to the left/right word boundary.
|-
| {{Key press|shift|home}}; {{Key press|shift|end}}
| Select to line start/end.
|-
| {{Key press|alt|shift|🡐}}; {{Key press|alt|shift|🡒}}
| Select to line start/end.
|-
| {{Key press|shift|🡑}}; {{Key press|shift|🡓}}
| Select up/down.
|-
| {{Key press|alt|k}}; {{Key press|alt|shift|k}}; <br>{{Key press|ctrl|alt|shift|🡐}}; {{Key press|ctrl|alt|shift|🡒}}
| Select word at cursor location or move to next/previous occurrence of selected text.
|-
| {{Key press|ctrl|alt|k}}
| Select all occurences of selected text.
|-
| {{Key press|ctrl|alt|🡐}}; {{Key press|ctrl|alt|🡒}}
| Add previous/next occurence of selected text to selection.
|-
| {{Key press|ctrl|shift|home}}; {{Key press|ctrl|shift|end}}
| Select to file start/end.
|-
| {{Key press|ctrl|shift|l}}
| Add the line of each cursor to the selection.
|-
| {{Key press|ctrl|shift|m}}
| Select everything inside matching brackets.
|-
| {{Key press|shift|page up}}; {{Key press|shift|page down}}
| Expand selection a page up/down.
|}
=====Multicursor=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|ctrl|alt|a}}
| Align all cursors with the right most cursor moving the content right of the cursor with it.
|-
| {{Key press|ctrl|alt|🡑}}; {{Key press|ctrl|alt|🡓}}
| Add cursor above or below.
|-
| {{Key press|ctrl|alt|shift|🡑}}; {{Key press|ctrl|alt|shift|🡓}}
| Move last added cursor up or down.
|}
=====Navigation=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|alt|🡐}}; {{Key press|alt|🡒}}
| Move cursor to line start/end.
|-
| {{Key press|🡑}}; {{Key press|🡓}}
| Move cursor to line above/below.
|-
| {{Key press|home}}; {{Key press|end}}
| Move cursor to line start/end.
|-
| {{Key press|🡐}}; {{Key press|🡒}}
| Move cursor to the left/right.
|-
| {{Key press|page up}}; {{Key press|page down}}
| Move the view a page up/down.
|-
| {{Key press|ctrl|🡑}}; {{Key press|ctrl|🡓}}
| Scroll up/down.
|-
| {{Key press|ctrl|home}}; {{Key press|ctrl|end}}
| Got to file start/end.
|-
| {{Key press|ctrl|p}}
| Jump to matching bracket.
|-
| {{Key press|ctrl|🡐}}; {{Key press|ctrl|🡒}}
| Move cursor to the left/right word boundary.
|}
=====Find/Replace=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|ctrl|k}}; {{Key press|ctrl|shift|k}}
| Find next/previous occurence of search string or selected text.
|-
| {{Key press|ctrl|f}}
| Find.
|}
=====Folding=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|f2}}; {{Key press|alt|f2}}
| Toggle fold of current structure.
|-
| {{Key press|alt|0}}
| Fold all fuctions and tables other than those containing the current active line or selection.
|-
| {{Key press|alt|l}}; {{Key press|alt|shift|l}}
| Toggle fold of selected text.
|-
| {{Key press|alt|shift|0}}
| Unfold all.
|}
=====Other=====
{| class="wikitable mw-collapsible mw-collapsed"
! Key combination
! Explanation
|-
| {{Key press|ctrl|u}}; {{Key press|ctrl|shift|u}}
| Change current word/selection to uppercase/lowercase.
|-
| {{Key press|tab}}; {{Key press|shift|tab}}
| Indent/outdent current line/selected lines.
|-
| {{Key press|ctrl|z}}
| Undo.
|-
| {{Key press|ctrl|y}}; {{Key press|ctrl|shift|z}}
| Redo.
|-
| {{Key press|ctrl|,}}
| Show settings menu of the ace editor. (Not usefull as settings aren't saved)
|-
| {{Key press|ctrl|shift|🡑}}; {{Key press|ctrl|shift|🡓}}
| Modify number up/down. Resolution of the steps depend on the cursor position in the number.
|-
| {{Key press|ctrl|space}}; {{Key press|ctrl|shift|space}}
| Open autocomplete window.
|}
===Debug console===
----
Every module page has a debug console, which you can see by scrolling down past the editor. The console has access to the module (including unsaved changes) as variable <code>p</code>. If possible, it is a good idea to test things here before saving. At the very least, you can input <code>=p</code> and press enter; if you get anything other than 'table' output, you have things to fix. It is also a good place to test things like patterns and refresh yourself on simple facts. You can use <code>mw.log()</code> to print strings and <code>mw.logObject()</code> to dump tables to the console, if necessary.
Most of the examples given in his guide can be tested by copying their code into the console. Use {{Key press|shift}}+{{Key press|enter}} to write multiline code before submitting.
See [[Help:Editing/Lua_guide#Frames|the ''Frames'' section]] for information on using the debug console with modules that use the frame (generally in the form <code>local args = frame:getParent().args</code>).
=Lua basics=
==First module==
All lua modules have to be written in the Module namespace - they cannot work if elsewhere. You can write test modules as a subpage of [[Module:Sandbox]], i.e. '''Module:Sandbox/User:USERNAME''' (for example <code>Module:Sandbox/User:{{USERNAME}}</code>). You can have additional subpages of this if you want, like your userpage (e.g. <code>Module:Sandbox/User:{{USERNAME}}/example</code>).
All modules follow a basic pattern: declare and initialise a master variable, assign functions to that variable, return the variable. By convention, the master variable is named <code>p</code>.
<syntaxhighlight lang='lua'>
-- <nowiki>
local p = {}
function p.hello( frame )
    return 'Hello, world!'
end
return p
-- </nowiki>
</syntaxhighlight>
This code first declares the master variable <code>p</code> using keyword <code>local</code>, and initialises it to an empty table <code>{}</code>. It then assigns a function named <code>hello</code> to <code>p</code>, with argument <code>frame</code> which simply returns the string ''Hello, world!''. <code>p</code> is then returned. The {{tag|nowiki|o}} comments prevent Mediawiki from parsing the module as wikicode and creating rogue links, it has no effect on the function of the module itself and it's considered best practice to add them.
If you save this module to ''Module:Sandbox/User:USERNAME'', you can then put <code><nowiki>{{#invoke:Sandbox/User:USERNAME|hello}}</nowiki></code> on any non-module page (e.g. your user space) to see the result (i.e. print ''Hello, world!'').
This should be a reasonable demonstration on a simple module; most parts of this will be covered more later.
==Basic syntax==
===Some lexical conventions===
----
Identifiers (or names) in Lua can be any string of letters, digits, and underscores, not beginning with a digit. (e.g. i, j, a10, _i, longerNames)
You should avoid identifiers starting with an underscore followed by one or more upper-case letters (e.g., _VERSION); they are reserved for special uses in Lua. Usually, the identifier _ (a single underscore) is used for dummy variables, and identifiers starting with a _ followed by something else (e.g. _hello) is used for internal use of a package.
The following words are reserved; we cannot use them as identifiers:
:{| class='wikitable'
|and
|break
|do
|else
|elseif
|-
|end
|false
|for
|function
|if
|-
|in
|local
|nil
|not
|or
|-
|repeat
|return
|then
|true
|until
|-
|while
|
|
|
|
|}
Lua is case-sensitive: <code>and</code> is a reserved word, but <code>And</code> and <code>AND</code> are two different identifiers.
Lua needs no separator between consecutive statements, but we can use a semicolon if we wish. Line breaks play no role in Lua's syntax; for instance, the following four chunks are all valid and equivalent:
<syntaxhighlight lang='lua'>
a = 1
b = a * 2
a = 1;
b = a * 2;
a = 1; b = a * 2
a = 1 b = a * 2 -- ugly, but valid
</syntaxhighlight>
You can assign values to multiple variables in one line using a comma separated list, this will be useful later for functions who return more than one variable:
<syntaxhighlight lang='lua'>
a, b, c = 1, 2, 3 -- a == 1, b == 2, c == 3
</syntaxhighlight>
Assignment expressions are evaluated before assigning, so <code>a, b = b, a</code> swaps the variable values.
===Comments===
----
Lua comments come in two types: single-line and multi-line. Anything written in a comment is not executed as code.
Single-line comments begin with <code>--</code> and last until the end of the line.
Multi-line comments begin with <code>--[=[</code> and end with <code>]=]</code>. Any number of equals signs can be between the brackets, including none, but the start and end need to have the same amount of equal signs.
===Data types===
----
To determine a data type of a variable, the <code>type( var )</code> function can be used; it will return the type as a string (e.g. 'nil').
====nil====
----
<code>nil</code> is the absence of value. You can't use it as a table key. When converted to a string, it is 'nil'; when converted to a number, it is still nil; when converted to a boolean, it is considered false - but remember, nil is not equal to false.
nil crops up in all sorts of places, so you'll need to remember to nil-check code. This is usually just a simple if statement:
<syntaxhighlight lang='lua'>
if variable then
    -- code if not nil
else
    -- code if nil
end
</syntaxhighlight>
or, if variable could legitimately be false:
<syntaxhighlight lang='lua'>
if variable ~= nil then
    -- code if not nil
else
    -- code if nil
end
</syntaxhighlight>
====boolean====
----
Boolean values are <code>true</code> and <code>false</code>. Their string representations are predictably 'true' and 'false'.
Notably, unlike some other languages, only <code>false</code> and <code>nil</code> are false values; the number 0 and the empty string are considered true.
====string====
----
Strings are a series of characters.
Literal strings - strings in the code directly - are enclosed by either <code>'</code> (apostrophes/single quotes) or <code>"</code> (double quotes). There is no difference between the two, though <code>'</code> is preferred, unless actual apostrophes are used in the string. String literals have some escape sequences, notably:
* <code>\t</code> tab
* <code>\n</code> newline
* <code>\'</code> quote
* <code>\"</code> double quote
* <code>\\</code> backslash
String literals can also be assigned as 'long strings' using the same bracket notation of comments (long strings do not interpret escape sequences):
<syntaxhighlight lang='lua'>
-- This long string
foo = [=[
bar\tbaz
]=]
-- is equivalent to this quote-delimited string
bar = 'bar\\tbaz\n'
</syntaxhighlight>
Strings have a few functions associated with them, covered later.
Any other data type can be converted to a string using the <code>tostring( value )</code> function. However, functions and tables simply return 'function' or 'table'<ref>The behaviour of the <code>tostring()</code> function on tables can be changed using the <code>__tostring</code> metatable index. More on that later.</ref>, respectively.
We can get the length of a string using the length operator (denoted by #):
<syntaxhighlight lang='lua'>
local a = 'hello'
mw.log( #a ) --> 5
mw.log( #'good bye' ) --> 8
</syntaxhighlight>
Strings are immutable values, any operation that would change the value of the string returns an entirely new string with is operands unchanged.
You can compare strings with the <code>==</code> operator:
<syntaxhighlight lang='lua'>
mw.log( 'hello' == 'hello' ) --> true
</syntaxhighlight>
====number====
----
Numbers are any numerical object - there is no 'int' and 'float' types, just number. They're internally represented as 64-bit floating point numbers ('double' in many other languages). Integer and decimal parts are separated with a period - <code>1234.56</code>. Alternatively, E-notation can be used - <code>123.45e10</code>.
NaN and infinities are handled correctly, but there are no literals available for them. If necessary, <code>0/0</code> generates a NaN, <code>1/0</code> generates positive infinity, and <code>-1/0</code> generates negative infinity.
Other data-types can be converted to a number using the <code>tonumber( value )</code> function. However, this is only really meaningful for string values - other data-types, and strings that can't be converted to a number, return nil.
====table====
----
Tables are associative arrays, similar to JavaScript objects, PHP arrays, Perl hashes and somewhat to Java HashMaps. This means items are stored as key-value pairs
Tables in Lua are neither values nor variables; they are objects. You may think of a table as a dynamically-allocated object; programs manipulate only references (or pointers) to them. Lua never does hidden copies or creation of new tables behind the scenes.
Tables are created with curly braces - the empty table is <code>{}</code>. Upon creation, tables can be filled using the following formats, separated by commas:
# <code>[expression1] = expression2</code> - the result of ''expression1'' is the key and the result of ''expression2'' is the value
#* Expression1 can be almost anything - a string with any characters, a number, a function, true/false, another table, etc.
# <code>name = expression</code> - equivalent to <code>['name'] = expression</code>
#* Note that this method only works if ''name'' is a simple string without characters that may otherwise be interpreted - essentially, if it contains anything other than alphanumeric characters (plus underscore), you'll need to use type 1
# <code>expression</code> - equivalent to <code>[auto incrementing number] = expression</code>
#* This notation is used to initialise tables with array like data in it. The auto incrementing number starts at 1.
Storing under a number is separate to storing under the string representation of that number. You cannot store under <code>nil</code>.
The value stored can be of any type. Storing the value nil is equivalent to removing the key from the table.
For example:
<syntaxhighlight lang='lua'>
local t = {
    a = 'value for key a',
    ['b'] = 'value for key b',
    ['c'] = variable_used_elsewhere,
    ['9'] = 'value for key string 9',
    [9] = 'value for key number 9',
    [true] = {'a sub-table stored under the value for boolean true'},
    [tonumber] = 'value for function tonumber',
    [tostring] = function_stored_under_tostring,
}
</syntaxhighlight>
You can also treat the table as a sequence of expressions (a traditional array):
<syntaxhighlight lang='lua'>
local t = {
    'a',
    'b',
    'c',
    'd',
    'e',
}
-- equivalent to
local t2 = {
    [1] = 'a',
    [2] = 'b',
    [3] = 'c',
    [4] = 'd',
    [5] = 'e',
}
</syntaxhighlight>
You can mix these two methods, but it is not advised for code clarity and the auto incrementing keys will overwrite manually defined keys. The length operator '#' will return the length of the array assuming there are not gaps in the number. If there is a gap its output is inconsistent; you will have to iterate over the entire table with <code>pairs</code> to check for the highest index:
<syntaxhighlight lang='lua'>
local t = {
    [1] = 'a',
    'b'
}
mw.log( t[1] )  --> 'b'
local t2 = {
    'a',
    'b',
    'c',
    [5] = 'd'
}
mw.log( #t2 )  --> 3 or 5
</syntaxhighlight>
After creation, you can retrieve and assign to table values in two ways:
* If the key is a simple string, you can use dot notation: <code>t.a</code>
* In all cases you can use bracket notation: <code>t['a']</code>
Bracket notation is how you'd access an index stored in a variable, e.g.:
<syntaxhighlight lang='lua'>
local t = {
    a = 'value for key a',
    ['b'] = 'value for key b',
}
mw.log( t['a'] )  --> 'value for key a'
mw.log( t.a )  --> 'value for key a'
local index = 'a'
mw.log( t[index] )  --> 'value for key a'
t[index] = 'new value'
mw.log( t[index] )  --> 'new value'
</syntaxhighlight>
You can mix array like keys with normal key-value pairs. The length operator '#' will only return the length of the array part.
<syntaxhighlight lang='lua'>
local t = {
    a = 'value for key a',
    ['b'] = 'value for key b',
    ['c'] = variable_used_elsewhere,
    ['9'] = 'value for key string 9',
    'a',
    'b',
    'c'
}
mw.log( #t )  --> 3
mw.log( t['a'] )  --> 'value for key a'
mw.log( t[1] )  --> 'a'
</syntaxhighlight>
You can append a value to the end of an array using <code>table.insert</code>:
<syntaxhighlight lang='lua'>
local t = {
    'a',
    'b',
}
table.insert( t, 'c' )
mw.log( t[3] )  --> 'c'
</syntaxhighlight>
<span id='Table_debugging'></span>While debugging, you can use <code>mw.logObject( table )</code> to print out he whole table in a readable format.
<syntaxhighlight lang='lua'>
local t = {
    a = 'value for key a',
    ['b'] = 'value for key b',
    ['9'] = 'value for key string 9',
    [true] = {'a sub-table stored under the value for boolean true'},
    'a',
    'b',
    'c'
}
mw.log( t )  --> 'table'
mw.logObject( t )  --[=[
                        table#1 {
                            'a',
                            'b',
                            'c',
                            [true] = table#2 {
                                'a sub-table stored under the value for boolean true',
                            },
                            ['9'] = 'value for key string 9',
                            ['a'] = 'value for key a',
                            ['b'] = 'value for key b',
                        }
                    ]=]
</syntaxhighlight>
<span id='Table_as_reference'></span>Tables are stored by reference in variables. This means that a variable only stores where in memory a table is located; it doesn't store all the data of the table. As a result multiple variables can point to the same table and a table is only really removed from memory when all variables linking to the table are destroyed. You can create a real copy of a table using the <code>mw.clone()</code> function.
<syntaxhighlight lang='lua'>
local t1 = { 1 }
local t2 = t1
local t3 = mw.clone( t1 )
t1[1] = 2
mw.log( t1[1] ) --> 2
mw.log( t2[1] ) --> 2
mw.log( t3[1] ) --> 1
t1 = nil
mw.log( t2[1] ) --> 2
t2 = nil -- Now the table is really destroyed, the cloned table t3 still exists
</syntaxhighlight>
=====Numbered or string keys=====
'''Numbered keys''', or sequences, are used when the order of your data is important but the exact location of it in the sequence is not. For example if you want to store a sentence word by word, it is important that the order of the words is maintained but we don't care which word is at a specific index:
<syntaxhighlight lang='lua'>
local t = { 'This', 'is', 'a', 'sentence', '.' }
local t2 = { 'This', 'is', 'a', 'longer', 'sentence', '.' }
</syntaxhighlight>
In the above example it is important that the word <code>This</code> is before <code>is</code>, but we don't care that the word <code>sentence</code> is at index 4 or 5.
'''String keys''' are used when the location of data is important. For example, when we want to store certain properties about an item:
<syntaxhighlight lang='lua'>
local ashes = {
    tradeable = true,
    equipable = false,
    stackable = false,
    value = 2,
    ['buy limit'] = 10000
}
</syntaxhighlight>
====function====
----
Functions can be treated as normal variables - assigned, overwritten, passed as arguments, created anonymously, etc.
Functions are created with <code>function</code>, and are covered more later.
===Structures===
----
====Operators====
----
The following operators are supported:
=====Arithmetic=====
* <code>+</code> addition
* <code>-</code> subtraction (or negation)
* <code>*</code> multiplication
* <code>/</code> division
* <code>%</code> modulo
* <code>^</code> exponentiation
When attempting to do arithmetic on a string, Lua will try to convert the strings to numbers with the <code>tonumber()</code> function; it is still advised to call the <code>tonumber()</code> function explicitly for code clarity.
<syntaxhighlight lang='lua'>
mw.log( 5 + '10' ) --> 15
</syntaxhighlight>
=====Relational=====
* <code>==</code> equality
* <code>~=</code> non-equality
* <code>></code> greater than
* <code><</code> less than
* <code>>=</code> greater than or equals
* <code><=</code> less than or equals
=====Logical=====
* <code>and</code>
* <code>or</code>
* <code>not</code>
<code>and</code> and <code>or</code> are short-circuit - <code>foo() or bar()</code> will only call <code>bar()</code> if <code>foo()</code> is false or nil.
Lua supports a conventional set of logical operators: <code>and</code>, <code>or</code>, and <code>not</code>. Like control structures, all logical operators consider both the Boolean <code>false</code> and <code>nil</code> as '''false''', and anything else as '''true'''. The result of the <code>and</code> operator is its first operand if that operand is false; otherwise, the result is its second operand. The result of the <code>or</code> operator is its first operand if it is not false; otherwise, the result is its second operand:
<syntaxhighlight lang='lua'>
mw.log( true and true ) --> true
mw.log( true and false ) --> false
mw.log( true and nil ) --> nil
mw.log( nil and true ) --> nil
mw.log( true and 4 ) --> 4
mw.log( 5 and 4 ) --> 4
mw.log( nil and 4 ) --> nil
mw.log( nil or 4 ) --> 4
mw.log( 4 or nil ) --> 4
mw.log( 4 or 5 ) --> 4
</syntaxhighlight>
This is very useful to set default values in case a variable is nil.
<syntaxhighlight lang='lua'>
local var = arg or 0 -- var == arg if arg is not nil, otherwise var == 0
local var = arg and math.pow( arg ) or 0 -- prevents 'math.pow' from throwing an error and store 0 in case arg == nil
</syntaxhighlight>
=====Concatenation=====
To join two strings together use <code>..</code>. When trying to concatenate a number and a string Lua will automatically convert the number to a string with the <code>tostring()</code> function.
<syntaxhighlight lang='lua'>
mw.log( 'Foo' .. 'Bar' ) --> 'FooBar'
local var1 = 'Foo'
local var2 = 'Bar'
mw.log( var1 .. var2 ) --> 'FooBar'
mw.log( 'Foo' .. 10 ) --> 'Foo10'
</syntaxhighlight>
If multiple concatenations are required, it may be faster and easier to use <code>string.format()</code> or <code>table.concat()</code> (see later).
=====Length=====
The length operator is <code>#</code>, used <code>#a</code>. If a is a string, the length of the string in bytes is returned. If a is a sequence, returns the number of entries in the array.
If a is table but not a strict sequence, it returns a value n such that a[n] is not nil and a[n+1] is nil - but this may not be consistent, so avoid using this operator on non-sequence tables.
=====Precedence=====
Order of operations is:
#^
#not # - (negation)
# * / %
#+ - (subtraction)
#.. (concatenation)
#< > <= >= ~= ==
#and
#or
====Functions====
----
The basic function is:
<syntaxhighlight lang='lua'>
local function ( variable_list )
    --block of code
end
</syntaxhighlight>
Functions can be given names in two ways:
# by assigning it to a variable: <code>do_this_thing = function ( variables ) ...</code>
# by putting the name just before the variable list: <code>function do_this_thing( variables ) ...</code>
These also apply to tables:
# <code>t.do_this_thing = function ( variables ) ...</code>
# <code>function t.do_this_thing( variables ) ...</code>
Newly declared variables in a function should always be local (more on that later). Variables declared outside the function can be used within it. Functions can be declared within other functions.
Values are returned from the function with the <code>return</code> keyword.
<syntaxhighlight lang='lua'>
local function addOne( num )
    local numplus1 = num + 1
    return numplus1
end
mw.log( addOne( 10 ) ) --> 11
</syntaxhighlight>
Functions can return comma-separated lists that aren't inside an array.
<syntaxhighlight lang='lua'>
local function addOneTwoThree( num )
    return num + 1, num + 2, num + 3
end
local a, b, c = addOneTwoThree( 10 ) --> a == 11, b == 12, c == 13
local d = addOneTwoThree( 10 ) --> d == 11, other two values thrown away
local _, e = addOneTwoThree( 10 ) --> e == 12, other two values thrown away, using the _ as a placeholder
</syntaxhighlight>
When used in control structures (e.g. <code>if addOneTwoThree( 0 ) then ... end</code>), only the first value is used.
When a function <samp>f</samp> returns the result of another function <samp>g</samp> which returns more than one result, but we are only interested in the first result of <samp>g</samp> we can wrap it in parenteses:
<syntaxhighlight lang='lua'>
local function g()
    return 1, 2
end
local function f()
    return ( g() )
end
local a, b = f() --> a == 1, b == nil
</syntaxhighlight>
It is allowed to supply more or less arguments than the function accepts:
<syntaxhighlight lang='lua'>
local function add( num, num2 )
    num = num or 0
    num2 = num2 or 0
    return num + num2
end
mw.log( add( 5 ) ) --> 5; num2 == nil
mw.log( add( 1, 2, 4 ) ) --> 3; 4 is ignored
</syntaxhighlight>
Functions are stored by reference and can be passed to other variables, tables and function arguments.
<syntaxhighlight lang='lua'>
local function addOne( num )
    return num + 1
end
local a = addOne
mw.log( a( 10 ) ) --> 11
local t = {
    foo = addOne
}
mw.log( t.foo( 10 ) ) --> 11
local function exec( func, num )
    return func( num )
end
mw.log( exec( addOne, 10 ) ) --> 11
</syntaxhighlight>
A Lua file is executed from top to bottom, therefore functions must declared before they are used:
<syntaxhighlight lang='lua'>
mw.log( addOne( 10 ) ) --> Error
local function addOne( num )
    return num + 1
end
mw.log( addOne( 10 ) ) --> 11
</syntaxhighlight>
It is possible to use a local functions inside another function before it is declared using forward declaration:
<syntaxhighlight lang='lua'>
local f  -- Forward declaration
local function addOne( num )
    return num + f()
end
function f()  -- No local keyword here since 'f' is already declared local higher up
    return 1
end
mw.log( addOne( 10 ) ) --> 11
</syntaxhighlight>
Global functions are implicitly forward declared.
You can create recursive local functions.
<syntaxhighlight lang='lua'>
-- This
local function factorial( num )
    if num <= 1 then
        return 1
    else
        return num * factorial( num - 1 )
    end
end
-- Is a syntactic sugar for
local factorial
factorial = function( num )
    if num <= 1 then
        return 1
    else
        return num * factorial( num - 1 )
    end
end
-- This does not work
local factorial = function( num )
    if num <= 1 then
        return 1
    else
        return num * factorial( num - 1 )  -- The variable 'factorial' is not yet defined so it tries to call the global variable instead of the local one
    end
end
</syntaxhighlight>
If a function is called with a single table as argument, it is possible to slightly shorten its notation by using <code>{}</code> instead of <code>()</code>:
<syntaxhighlight lang='lua'>
local function sum( t )
    ...
end
sum( { name = value } )
sum{ name = value }
</syntaxhighlight>
<span id='Function_output_as_function_argument'>The output of one function can be used as the input of another</span>. Take for example the following code:
<syntaxhighlight lang='lua'>
local function foo( num )
    mw.log( 'foo' )
    return num
end
local function bar( num )
    mw.log( 'bar' )
    return num * 2
end
local result = bar( foo( 10 ) ) --> result == 20
--[=[ Prints: 'foo'
              'bar'
]=]
</syntaxhighlight>
The mw.log statements allow us to track which function is executed first. Looking at the printed text, it is clear that first the function <code>foo</code> is executed, printing its name and returning the value 10. This value is then passed into function <code>bar</code> which prints its own name and returns the value 20, which is then stored in the variable <code>result</code>. So <code>result</code> only gets to see the output of the very outer function and never gets to see anything that happened before the <code>bar</code> function ended.
The standard libraries are just functions stored in tables, e.g. <code>math.sin()</code> means, in table <code>math</code> executed the function stored at key <code>sin</code>.
Lua also offers a special syntax for object-oriented calls, the colon operator. An expression like <code>o:foo()</code> calls the method <code>foo</code> in the object <code>o</code>. (more on this later)
====Varargs====
----
Varargs (variable arguments) can be used by functions that should work on any number of passed arguments, but the number is not known ahead of time. They are represented by using <code>...</code> in the function:
<syntaxhighlight lang='lua'>
local function sum( ... )
    local t = { ... }
local s = 0
for i, v in ipairs( t ) do  -- More on this later
  s = s + v
end
return s
end
mw.log( sum( 4, 5, 6, 7, 8, 9 ) ) --> 39
</syntaxhighlight>
In the function declaration, <code>...</code> has to be the last 'parameter'. Additional parameters can come first, though. Inside the function, the list of varargs is referenced by <code>...</code>. It is common, as in the example above, to turn the varargs into a sequence by wrapping it in curly braces. Then it can be iterated over by ipairs.
====Control structures====
----
Control structures come in two main flavours: conditional and loops.
=====if=====
If statements are of the generic form:
<syntaxhighlight lang='lua'>
if expression1 then
    --block if expression1 is true
elseif expression2 then
    --block if expression1 is false and expression2 is true
else
    --block if all expressions are false
end
</syntaxhighlight>
The elseif and else sections can be omitted if they are not needed.
Each expression should result in a boolean, but remember that all values can evaluate to a boolean: false and nil are false, and all other values are true (including 0, the empty string, and the empty table).
e.g.:
<syntaxhighlight lang='lua'>
local var = 1
if var == 0 then
    mw.log( 'if' )
elseif var > 10 then
    mw.log( 'elseif' )
else
    mw.log( 'else' )
end
--> 'else'
</syntaxhighlight>
=====while=====
While loops repeat a given block until an expression returns false.
<syntaxhighlight lang='lua'>
while expression do
    --block to evaluate while expression is true
end
</syntaxhighlight>
e.g.:
<syntaxhighlight lang='lua'>
local i = 10
local fib = 1
while i > 0 do
    fib = fib + fib
    i = i - 1  -- Very important, without this the loop would go on forever
end
mw.log( fib ) --> 1024
</syntaxhighlight>
=====repeat=====
Repeat is essentially the reverse of a while loop - it repeats the block until the expression is true.
<syntaxhighlight lang='lua'>
repeat
    --block to evaluate while expression is false
until expression
</syntaxhighlight>
=====for=====
For loops repeat a number of times. There are two forms of for - the second of which is under the for-each heading below.
<syntaxhighlight lang='lua'>
for name = start, stop, step do
    --block to evaluate
end
</syntaxhighlight>
This version declares the local variable <code>name</code>, assigns it value <code>start</code>, then iterates the block, adding <code>step</code> to <code>name</code> each iteration until <code>name</code> exceeds <code>stop</code>. All three values can be expressions that evaluate to numbers.
<code>step</code> may be omitted to use the default value of 1.
For <code>name</code> the values <code>i</code> and <code>j</code> are commonly used.
e.g.:
<syntaxhighlight lang='lua'>
local var = 0
for i = 10, 0, -1 do
    var = var + i
end
mw.log( var ) --> 55
</syntaxhighlight>
=====for-each=====
For each loops are primarily for table iteration. They require iterator functions, which are handily provided by the <code>pairs</code> and <code>ipairs</code> functions. You can use custom functions, but that is beyond the scope of this guide.
<syntaxhighlight lang='lua'>
for k, v in pairs( t ) do
    --block to evaluate
end
</syntaxhighlight>
This loop then iterates over the block, setting <code>k</code> to the key and <code>v</code> to the associated value for each entry in the table.
The <code>pairs()</code> function provides iteration over all values in the tables. The order is not reliable or consistent, so if order is important you may need another way.
The <code>ipairs()</code> function provides iteration over sequences. It will always iterate in the order of the sequence, if the sequence contains gaps it will stop at the first gap. All non number keys are ignored.
To order a non-sequence, you can use the <code>opairs</code> function of [[Module:Iterator]]. If you want a more controlled order, you can set a manual order in a sequence (or iterate over the table a few times, first to get the keys/values in a sequence and order them with <code>table.sort</code>, then to iterate in the order).
e.g.:
<syntaxhighlight lang='lua'>
local t = {
    a = 'a',
    b = 'b',
    'c',
    'd'
}
for k, v in pairs( t ) do
    mw.log( 'key: ' .. k .. '; value: ' .. v )
end
for i, v in ipairs( t ) do
    mw.log( 'index: ' .. i .. '; value: ' .. v )
end
--[[
key: 1; value: c
key: 2; value: d
key: a; value: a
key: b; value: b
index: 1; value: c
index: 2; value: d
]]
</syntaxhighlight>
===Scoping===
----
In computer programming, the scope of a name binding—an association of a name to an entity, such as a variable—is the region of a computer program where the binding is valid: where the name can be used to refer to the entity. Such a region is referred to as a scope block. In other parts of the program the name may refer to a different entity (it may have a different binding), or to nothing at all (it may be unbound).<ref>https://en.wikipedia.org/wiki/Scope_(computer_science)</ref>
In short, scope is the concept where only parts of your program have access to certain variables. e.g.:
<syntaxhighlight lang='lua'>
for i = 1, 10 do
    ...
end
mw.log( i ) --> nil, we don't have access to the variable 'i' outside the for loop
</syntaxhighlight>
With scoping it is possible to re-use the same variable name in different scope blocks; this is not recommended for code clarity but it explains the concept well.
<syntaxhighlight lang='lua'>
local var = 'top level'
if true then
    mw.log( var ) --> 'top level', we can access variables defined in a higher level scope
    local var = 'level 1' -- This variable is restricted to the scope of the first if statement, the top level variable is unchanged but we can no longer access it from within this scope
    mw.log( var ) --> 'level 1'
   
    if true then
        mw.log( var ) --> 'level 1'
        local var = 'level 2'
        mw.log( var ) --> 'level 2'
    end
   
    mw.log( var ) --> 'level 1'
end
mw.log( var ) --> 'top level'
</syntaxhighlight>
All control structures (e.g. if, for, ...) and functions create a new scope.
===Global or local===
----
Global variables do not need declarations; we simply use them. It is not an error to access a non-initialized variable; we just get the value nil as the result:
<syntaxhighlight lang='lua'>
mw.log( b ) --> nil
b = 10
mw.log( b ) --> 10
</syntaxhighlight>
If we assign nil to a global variable, Lua behaves as if we have never used the variable:
<syntaxhighlight lang='lua'>
b = nil
mw.log( b ) --> nil
</syntaxhighlight>
Lua does not differentiate a non-initialized variable from one that we assigned nil. After the assignment, Lua can eventually reclaim the memory used by the variable.
Global variables are stored in the <code>_G</code> table. Global variables always have global scope; this means once it is created every scope has access to it until it is manually deleted:
<syntaxhighlight lang='lua'>
mw.log( var ) --> nil
local function test()
    var = 'level 1'  -- Global variable
end
test()
mw.log( var ) --> 'level 1'
</syntaxhighlight>
We can localise a variable to a given scope with the <code>local</code> keyword. Doing so has a few advantages:
* Speed. It is faster to access a local variable.
* Memory saving. Once we exit the scope in which the local variable was created it is automatically destroyed.
* Preventing naming collisions or unexpected behaviour.
<syntaxhighlight lang='lua'>
function main()
a = 1  -- A global variable
    local b = 2  -- A local variable
test()
mw.log( a )  --> 10
    mw.log( b )  --> 2
end
function test()
a = 10  -- Also a global variable
    local b = 20
end
</syntaxhighlight>
The above example shows the problem with global variables. In both functions the variable <code>a</code> points to the '''same''' global variable, so the function <code>test</code> unintentionally changes the internal variable of the function <code>main</code>. For this reason you should '''always''' localise your variables.
It is also a good idea to localize your functions to prevent you from accidentally overwriting a function with the same name inside a module you require, demonstrated by the following example:
;<nowiki>Module:Foo</nowiki>
<syntaxhighlight lang='lua'>
-- <nowiki>
local p = {}
function printName()  -- Global function
    mw.log( 'Foo' )
end
local function printName2()  -- Local function, this means it can only be used inside this module
    mw.log( 'Foo' )
end
function p.name()
printName()
end
function p.name2()
    printName2()
end
return p
-- </nowiki>
</syntaxhighlight>
;<nowiki>Module:Bar</nowiki>
<syntaxhighlight lang='lua'>
-- <nowiki>
local foo = require( 'Module:Foo' )  -- More about this later
local p = {}
function printName()  -- Global function
    mw.log( 'Bar' )
end
local function printName2()  -- Local function
    mw.log( 'Bar' )
end
function p.main()
    printName() --> 'Bar'
    printName2() --> 'Bar'
    foo.name() --> 'Bar'
    foo.name2() --> 'Foo'
end
return p
-- </nowiki>
</syntaxhighlight>
The function <code>printName</code> in <code>Module:Foo</code> is overwritten by the function in <code>Module:Bar</code>.
==Scribunto==
In Scribunto modules are called using the #invoke parser function with the following syntax: <code><nowiki>{{#invoke:<module name>|<function to call>|<arg>}}</nowiki></code>
* <code><module name></code> - name of the module without the <code>Module:</code> namespace prefix.
* <code><function to call></code> - the function you want to call must be inside a table that is returned by the module; usually this table is called <code>p</code>. Functions outside this table can't be accessed by #invoke.
* <code><args></code> - same style of argument you can pass to templates. This is optional.
===Frames===
----
So far, the above has been about core lua. Now, we need to consider how this applies to the wiki - the most important of which is how modules are called.
Think of this as some sort of layer cake. Imagine the following example template and module are real and called from this page (you can test it yourself by copying the example to your userspace and sandbox module; see [[#First_module]]):
<code><nowiki>{{Mytemplate|text|3|separator=;}}</nowiki></code>: text;text;text;suffix
;<nowiki>Template:Mytemplate</nowiki>
<code><nowiki>{{#invoke:Mymodule|main|last=suffix}}</nowiki></code>
;<nowiki>Module:Mymodule</nowiki>
<syntaxhighlight lang='lua'>
-- <nowiki>
local p = {}
function p.main( frame )
    local invokeArgs = frame.args
    local args = frame:getParent().args  -- Template args are customary stored in the 'args' variable
    mw.log( args[1] ) --> 'text'
    mw.log( args[2] ) --> '3'
    mw.log( args.separator ) --> ';'
    mw.log( invokeArgs.last ) --> 'suffix'
    local text = args[1] or ''  -- Set default value in case no parameters were passed to the template
    local multiplier = tonumber( args[2] ) or 0
    local separator = args.separator or ''
    local last = invokeArgs.last or ''
    local res = ''
    for i = 1, multiplier do
        res = res .. text .. separator
    end
    return res .. last
end
return p
-- </nowiki>
</syntaxhighlight>
# Right here, this page calls '''Template:Mytemplate''' with parameter <code>1</code> set to <code>text</code>, parameter <code>2</code> set to <code>3</code> and parameter <code>separator</code> set to <code>;</code>.
# This then loads up '''Template:Mytemplate''' and passes the parameters. In a normal wikicode template, this is where it does parsing - possibly with other templates helping - and returns up to the page. But, as ''Mytemplate'' uses #invoke, it moves down to the lua layer.
# The #invoke called '''Module:Mymodule''''s <code>main</code> function, with an additional parameter of <code>last</code> set to <code>suffix</code>. So, it executes the function <code>p.main</code>. Once it has the result, it passes it back up to the template, and the template passes it up to the page.
Now, how does '''Module:Mymodule''' get both sets of parameters? This is from the main variable passed to the function, the frame. Frames are tables with a few functions and values.
# The #invoke call generates a frame for itself, with the notable part being the <code>args</code> value. Since this #invoke specifies a parameter, it gets <code>args = {last = 'suffix'}</code>.
# The #invoke also generates a frame for the template call, which has the <code>args</code> value set to <code>args = { [1] = 'text', [2] = '3', separator = ';' }</code>. This frame is set as a parent to the previous frame.
# The <code>main</code> function in '''Module:Mymodule''' is passed the child frame. The parent can be accessed using the <code>getParent()</code> function.
Thus, in this example:
* <code>frame.args</code> = <code>{ last = 'suffix' }</code>
* <code>frame:getParent().args</code> = <code>{ [1] = 'text', [2] = '3', separator = ';' }</code>
;Notes
* Unnamed template parameters are passed as a sequence.
* Argument values are always strings.
* <code>getParent()</code> will return nil if there is no parent - e.g. if #invoke is directly used, or in the debug console.
;Debug console
When we call the main function from the debug console, we would have to create our own dummy frame. To make it easier to use the debug console it is customary to split the bulk of the code from the frame using a _main function. e.g.:
<syntaxhighlight lang='lua'>
-- <nowiki>
local p = {}
function p.main( frame )
    local invokeArgs = frame.args
    local args = frame:getParent().args
   
    return p._main( invokeArgs, args )
end
function p._main( invokeArgs, args )
    local text = args[1] or ''
    local multiplier = tonumber( args[2] ) or 0
    local separator = args.separator or ''
    local last = invokeArgs.last or ''
    local res = ''
    for i = 1, multiplier do
        res = res .. text .. separator
    end
    return res .. last
end
return p
-- </nowiki>
</syntaxhighlight>
If you need to test a function that does not use the above method you can create a frame object in the console. Copy the following code into the console, replacing the <code>-- Args go here as ['name'] = value,</code> with appropriate args and the <code>p.main</code> call with the correct function name:
<syntaxhighlight lang='lua'>
local frame = {}
function frame.getParent()
local args = {
-- Args go here as ['name'] = value,
}
return { args = args }
end
mw.log( p.main(frame) )
</syntaxhighlight>
===Importing===
----
There are a number of modules already written which provide functionality you may need. There are two ways of accessing this.
====Requiring modules====
----
The core function <code>require</code> is the general-purpose module importer. To require a module, simply use the full page name of the module you need as the only parameter. This returns whatever the module returns (usually the <code>p</code> table, but some modules return a single function). You can then use the functions/data of the module as normal.
<syntaxhighlight lang='lua'>
-- import [[Module:Paramtest]]
local paramtest = require( 'Module:Paramtest' )
-- use the has_content function of Paramtest
paramtest.has_content( args.foo )
</syntaxhighlight>
If you only need to use one function from the module, you can assign that function to a local variable:
<syntaxhighlight lang='lua'>
local hc = require( 'Module:Paramtest' ).has_content
hc( args.foo )
</syntaxhighlight>
You can import any module, but ones designed with common functions to be imported are called 'helper modules'; you can see a list of these at [[RuneScape:Lua/Helper modules]].
====Loading data====
----
If you're loading data - not functions - it is more efficient to use the Scribunto function <code>mw.loadData</code>. This functions identically to <code>require</code>, except for a few conditions:
* The module is evaluated once per page load, instead of once per #invoke (thus making it more efficient when repeatedly used on a page)
* The module must return a table, and that table (and all subtables) can only contain numbers, strings, booleans, or more tables - notably, functions are not allowed. The keys of all tables must be booleans, numbers, or strings.
* The table returned is read-only
<syntaxhighlight lang='lua'>
local data = mw.loadData( 'Module:Experience/elitedata' )
</syntaxhighlight>
Data is usually stored as a subpage of the primary module. Some examples of data pages:
* [[Module:Experience/elitedata]] is a simple list
* [[Module:Globals/data]] is a table of tables
* [[Module:Experience/data]] generates all the values from the level-experience formula
* [[Module:Disassemble/data]] defines a main data table, then does additional parsing on that table to create the actual return value
If you're using data, you should absolutely try to use <code>mw.loadData</code> over <code>require</code> or putting it into the module directly. The only exception is when you need to access this data a lot in a tight loop because a side effect of making the table read only is that its access time significantly increases (times 12 if reading a key that exists, times 6 otherwise).
====Documentation====
----
Documentation of a module can be edited on the <code>/doc</code> subpage of the module. It is good practice to always add a doc page with at least {{t|Documentation}} or {{t|No documentation}} on it. Those templates will then generate a dependency list based on the content of your module which makes it a lot easier to find out which modules rely on each other.
See some examples of this:
* [[Module:DropsLine]]
* [[Module:GEPrices/data.json]]
* [[Template:DropsLine]]
* [[Module:Infobox]]
=Advanced lua=
==Tail calls==
A feature of functions in Lua is that Lua does tail-call elimination. A <samp>tail call</samp> is a goto dressed as a call. A tail call happens when a function calls another as its last action, so it has nothing else to do. For instance, in the following code, the call to <code>g</code> is a tail call:
<syntaxhighlight lang='lua'>
function f( x )
    x = x + 1
    return g( x )
end
</syntaxhighlight>
After <samp>f</samp> calls <samp>g</samp>, it has nothing else to do. In such situations, the program does not need to return to the calling function when the called function ends. Therefore, after the tail call, the program does not need to keep any information about the calling function on the stack. When <samp>g</samp> returns, control can return directly to the point that called <samp>f</samp>. Some language implementations, such as the Lua interpreter, take advantage of this fact and actually do not use any extra stack space when doing a tail call. We say that these implementations do ''tail-call elimination''.
Because tail calls use no stack space, the number of nested tail calls that a program can make is unlimited. For instance, we can call the following function passing any number as argument:
<syntaxhighlight lang='lua'>
function foo( n )
    if n > 0 then
        return foo( n - 1 )
    end
end
</syntaxhighlight>
It will never overflow the stack.
A subtle point about tail-call elimination is what is a tail call. Some apparently obvious candidates fail the criterion that the calling function has nothing else to do after the call. For instance, in the following code, the call to g is not a tail call:
<syntaxhighlight lang='lua'>
function f( x )
    g( x )
end
</syntaxhighlight>
The problem in this example is that, after calling g, f still has to discard any results from g before returning. Similarly, all the following calls fail the criterion:
<syntaxhighlight lang='lua'>
return g( x ) + 1 -- must do the addition
return x or g( x ) -- must adjust to 1 result
return ( g( x ) ) -- must adjust to 1 result
</syntaxhighlight>
In Lua, only a call with the form <code>return func( args )</code> is a tail call. However, both <samp>func</samp> and its arguments can be complex expressions, because Lua evaluates them before the call. For instance, the next call is a tail call:
<syntaxhighlight lang='lua'>
return x[i].foo( x[j] + a*b, i + j )
</syntaxhighlight>
Tail calls use no stack space, this means Lua does also not keep any information about the name of the function they call or at which line the call happened. Therefor the info Lua can give in a stack traceback is limited, in fact the only info it can give is that a tail call happened and nothing more. For example take the following code:
<syntaxhighlight lang='lua' line>
local p = {}
function p.foo()
    return p.bar() -- Tail call one
end
function p.bar()
    return p.buz() -- Tail call two
end
function p.buz()
    return 1 + nil -- This will throw an error
end
return p
</syntaxhighlight>
If we call function <samp>foo</samp> using <code>p.foo()</code> in the console, we get:
<pre>
Lua error at line 12: attempt to perform arithmetic on a nil value.
Backtrace:
    Module:Sandbox/User:USERNAME:12: ?
    (tail call): ?
    (tail call): ?
    console input:7: ?
    [C]: ?
</pre>
In comparison when not using tail calls:
<syntaxhighlight lang='lua' line>
local p = {}
function p.foo()
    p.bar()
end
function p.bar()
    p.buz()
end
function p.buz()
    return 1 + nil -- This will throw an error
end
return p
</syntaxhighlight>
If we call function <samp>foo</samp> using <code>p.foo()</code> in the console, we get:
<pre>
Lua error at line 12: attempt to perform arithmetic on a nil value.
Backtrace:
    Module:Sandbox/User:USERNAME:12: in function "buz"
    Module:Sandbox/User:USERNAME:8: in function "bar"
    Module:Sandbox/User:USERNAME:4: in function "foo"
    console input:7: ?
    [C]: ?
</pre>
==Closures==
===First-class values===
----
Functions are first-class values in Lua. This means that functions can be stored and passed around like normal variables. On the hardware level this is achieved by storing the instructions of the function in memory at a known location and then storing this memory address in a variable. So when we then copy this variable or pass it as an argument to a function all we do is copy the address to the function, the content of the function is never moved. Lua will also keep track of all the variables that point to this function and only when all references are gone the function is deleted from memory. Now since Lua is a dynamically typed language (the type of a variable can change at runtime) this is a little bit more complex in reality as it also need to keep track of what data type this memory address points to, but the general idea still applies.
<syntaxhighlight lang='lua'>
-- Create a nameless function in memory which returns the value 0.
-- Then store the memory address of this function in the local variable 'foo'.
-- If for example the function is located at address 0x00001000, then foo = (function pointer 0x00001000)
local function foo()
    return 0
end
local bar = foo -- bar == (function pointer 0x00001000)
</syntaxhighlight>
===Lexical scoping===
----
What does it mean for functions to have "lexical scoping"? It means that functions can access variables of their enclosing functions. The reason this is special is because functions are first-class variables which means that a function can escape the scope it was created in. So it also means that a function can bind/capture local variables of its enclosing scope to be used later even when the scope where the local variable was created no longer exists!
<syntaxhighlight lang='lua'>
local names = {'Peter', 'Paul', 'Mary'}
local grades = {Mary = 10, Paul = 7, Peter = 8}
table.sort( names, function( n1, n2 )
    return grades[n1] > grades[n2] -- compare the grades
end )
</syntaxhighlight>
In the above example the anonymous function (also called a lambda) that is used for sorting has access to the variable <samp>grades</samp> even though it is never passed to the function as an argument.
Since functions can bind variables to itself it is possible to make functions that sort of behave like objects with their own local variables.
<syntaxhighlight lang='lua'>
local function foo( num )
    local n = num or 0
    local function bar()
        mw.log( n )
        n = n + 1
    end
    return bar
end
local a = foo( 5 )
local b = foo( 10 )
a() --> 5
b() --> 10
a() --> 6
b() --> 11
</syntaxhighlight>
The function <samp>bar</samp> binds the variable <samp>n</samp> to itself, and each instance of bar gets its own version of <samp>n</samp>. We call <samp>bar</samp> a closure. The function <samp>foo</samp> does not access any outer variables so it is not a closure but just a normal function. Creating a closure is slightly more expensive than a normal function but in most cases you don't need to worry about it.
==Generic for==
By now you are probably familiar with the <samp>pairs</samp> and <samp>ipairs</samp> functions to iterate over a table with a for-loop. Now Lua allows you to write your own functions to iterate over using a for-loop ([[Module:Iterator]]). The syntax for this generic for is as follows:
<syntaxhighlight lang='lua'>
for var_list in _f, _t, _var do
    ...
end
</syntaxhighlight>
<samp>var_list</samp> is a list of one or more comma separated values. <samp>_f</samp> is a function that is called with each iteration. <samp>_t</samp> is the first argument to <samp>_f</samp>, usually this is a table we want to iterate over. <samp>_var</samp> is the second argument to <samp>_f</samp>, this is used to keep track of the current state. Usually we use a single function that returns the values for those three slots.
<syntaxhighlight lang='lua'>
local myTable = {'a', 'b', 'c'}
local _f, _t, _var = ipairs( myTable ) -- _t == myTable, _var == 0
for i, v in _f, _t, _var do
    mw.log( i, v )
end
-- 1 a
-- 2 b
-- 3 c
</syntaxhighlight>
In general, a for-loop like
<syntaxhighlight lang='lua'>
for var_1, ..., var_n in explist do
    -- block
end
</syntaxhighlight>
Is equivalent to
<syntaxhighlight lang='lua'>
do
    local _f, _t, _var = explist
    while true do
        local var_1, ..., var_n = _f( _t, _var )
        _var = var_1
        if _var == nil then
            break
        end
        -- block
    end
end
</syntaxhighlight>
===Stateless iterator===
----
As the name implies, a stateless iterator is an iterator that does not keep any state by itself. Therefore, we can use the same stateless iterator in multiple loops, avoiding the cost of creating new closures.
As we just saw, the for-loop calls its iterator function with two arguments, one is the table we are iterating over and the other is a state variable. An example of this kind of iterator is <samp>ipairs</samp>. Now lets implement our own <samp>ipairs</samp> function.
<syntaxhighlight lang='lua'>
local function iter( t, i )
    i = i + 1
    local v = t[i]
    if v then
        return i, v
    end
end
function ipairs( t )
    return iter, t, 0
end
</syntaxhighlight>
Every iteration of the for-loop the iter function is called. The first time the value of the argument <samp>i</samp> is 0; the function then returns <code>1, t[1]</code>. The second loop <samp>i</samp> is 1 and the function returns <code>2, t[2]</code>. This continues untill the table <samp>t</samp> has run out of elements and returns <samp>nil</samp>; <samp>iter</samp> then also returns nil. This signals the for-loop that the iteration is done.
===Closure as iterator===
----
It is not uncommon that the iterator function needs more than one state variable and/or it needs a state variable that is not meant to be exposed to the outside. In this case we can use a closure as iterator function since as we saw before a closure can remember variables. Lets make another implementation of <samp>ipairs</samp> but using a closure this time.
<syntaxhighlight lang='lua'>
function ipairs( t )
    local i = 0
    return function()
        i = i + 1
        local v = t[i]
        if v then
            return i, v
        end
    end
end
</syntaxhighlight>
Since we are using a closure now the iterator function no longer needs any arguments and thus <samp>ipairs</samp> does not need to return a value for <samp>_t</samp> and <samp>_var</samp>. The biggest downside of this method is that we need to create a new closure for every for-loop that uses this <samp>ipairs</samp> implementation whereas the stateless iterator only has to create its <samp>iter</samp> function once.
==Metatables==
==Object oriented programming==
==Performance optimizations==
===Function profiling===
----
==Semantic mediawiki==
=See also=
* [[Wikipedia:Lua (programming language)]] and [[Wikipedia:Project:Lua]], for a more in-depth breakdown of the coding Language
* [[mw:Extension:Scribunto/Lua reference manual|Lua reference manual on MediaWiki]], for the documentation of Lua as used by the Scribunto extension, including standard functions
* [[RuneScape:Lua/Helper modules]] a list of all our modules designed to help with writing other modules
* [[RuneScape:Lua/Library functions]] Examples for most standard functions, as well as our own helper modules (exchange, maps etc)
* [[RuneScape:Lua/Modules]], for a list of all modules, excluding Exchange and miscellaneous data pages
* [[:Category:Lua-based templates]], for an index of templates that directly invoke Lua
* [[Special:PrefixIndex/Module:]], for a list of all current modules
* [[Special:PrefixIndex/Module:Data/]], for a list of modules containing data for the data namespace
* [[Special:PrefixIndex/Module:Sandbox/]], for a list of personal test modules
{{Editing help}}
=Footnotes=
<references/>
{{Editing guides}}
{{Lua guides}}

Latest revision as of 22:13, 2 November 2024

Important

The primary lua reference guide for this is Extension:Scribunto/Lua reference manual on MediaWiki which gives a short description of how to use every function in the standard libraries. While this guide will cover some of this, it will focus more on how to use lua as a whole rather than explaining the use of every available function. For some more explanation on the standard library functions see RuneScape:Lua/Library functions.