(→Metatables: note recent addition of getmetatable()) |
(Adding categories) |
||
(28 intermediate revisions by 11 users not shown) | |||
Line 1: | Line 1: | ||
Lua is a programming language designed primarily for embedded systems. It is popular in the video game industry as a language that can be embedded in a larger game engine. |
Lua is a programming language designed primarily for embedded systems. It is popular in the video game industry as a language that can be embedded in a larger game engine. |
||
− | + | PICO-8 implements a subset of Lua for writing game cartridges. Because it is a subset, not all features of Lua are supported. Most notably, PICO-8 does not include the Lua standard library, and instead provides a proprietary collection of global functions. |
|
* [https://www.lua.org/docs.html Official Lua documentation] |
* [https://www.lua.org/docs.html Official Lua documentation] |
||
* [https://en.wikipedia.org/wiki/Lua_(programming_language) Lua (Wikipedia)] |
* [https://en.wikipedia.org/wiki/Lua_(programming_language) Lua (Wikipedia)] |
||
− | = Features of |
+ | = Features of PICO-8 Lua = |
− | The following are the major language features of Lua as implemented by |
+ | The following are the major language features of Lua as implemented by PICO-8. |
== Comments == |
== Comments == |
||
− | + | PICO-8 code comments are preceded by two hyphens (<code>--</code>) and go to the end of the line. They also support a multi-line syntax using double-brackets (<code><nowiki>--[[ ... ]]</nowiki></code>). |
|
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
-- one-line comment |
-- one-line comment |
||
--[[ multi-line |
--[[ multi-line |
||
comment ]] |
comment ]] |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
== Values == |
== Values == |
||
− | + | PICO-8 supports these fundamental types of values: |
|
− | * Numbers, in the range supported by 16 |
+ | * Numbers, in the range supported by signed 16.16 bit fixed point: |
− | ** |
+ | ** Minimum value: hex <code>0x8000.0000</code>, decimal <code>-32768.0</code> |
+ | ** Maximum value: hex <code>0x7fff.ffff</code>, decimal <code>32767.9999847412109375</code> |
||
+ | ** Number literals can use decimal (<code>17.25</code>) or hexadecimal (<code>0x11.4</code>). |
||
+ | ** Things to be aware of: |
||
+ | *** PICO-8 prints and converts numbers to decimal strings by rounding off to four decimal places. This has the side effect that the maximum number rounds up to <code>32768.0</code>, which is actually not a valid PICO-8 number. It also means that the tiniest negative fraction below 0 displays as the somewhat-unintuitive <code>-0</code>. |
||
+ | *** This range is narrow in comparison to modern platforms. Be aware of situations where you might overflow, e.g. calculating the distance between corners of the screen requires calculating 128*128+128*128, which will overflow and wrap to -32768. |
||
* Booleans (<code>true</code> and <code>false</code>) |
* Booleans (<code>true</code> and <code>false</code>) |
||
* Strings |
* Strings |
||
Line 41: | Line 46: | ||
== Arithmetic operators == |
== Arithmetic operators == |
||
− | + | PICO-8 supports these arithmetic operators: |
|
− | * |
+ | * addition: <code>a + b</code> |
− | * |
+ | * subtraction: <code>a - b</code> |
− | * |
+ | * multiplication: <code>a * b</code> |
− | * |
+ | * division: <code>a / b</code> |
* modulo: <code>a % b</code> |
* modulo: <code>a % b</code> |
||
− | * |
+ | * exponentiation: <code>a ^ b</code> |
Arithmetic operators take numbers as arguments and return a number. |
Arithmetic operators take numbers as arguments and return a number. |
||
Line 54: | Line 59: | ||
== Relational operators == |
== Relational operators == |
||
− | + | PICO-8 supports these relational operators: |
|
* less than: <code>a < b</code> |
* less than: <code>a < b</code> |
||
* greater than: <code>a > b</code> |
* greater than: <code>a > b</code> |
||
− | * less than or equal: <code>a <= b</code> |
+ | * less than or equal to: <code>a <= b</code> |
− | * greater than or equal: <code>a >= b</code> |
+ | * greater than or equal to: <code>a >= b</code> |
* equal: <code>a == b</code> |
* equal: <code>a == b</code> |
||
− | * not equal: <code>a ~= b</code>; |
+ | * not equal: <code>a ~= b</code>; PICO-8 synonym: <code>a != b</code> |
Relational operators take numbers as arguments and return either <code>true</code> or <code>false</code>. |
Relational operators take numbers as arguments and return either <code>true</code> or <code>false</code>. |
||
Line 67: | Line 72: | ||
== Logical operators == |
== Logical operators == |
||
− | + | PICO-8 supports the following logical operators: |
|
* and: <code>a and b</code> |
* and: <code>a and b</code> |
||
Line 73: | Line 78: | ||
* not: <code>not a</code> |
* not: <code>not a</code> |
||
− | In logical expressions, <code>false</code> and <code>nil</code> are false, all other values are true. |
+ | In logical expressions, both <code>false</code> and <code>nil</code> are treated as false, and all other values are treated as true. |
− | Logical expressions "short circuit," |
+ | Logical expressions "short circuit," which is to say they stop evaluating expressions left to right as soon as the value of the expression is known. For example, "<code>is_alive() and can_shoot()"</code> will first call <code>is_alive()</code>, and will only call <code>can_shoot()</code> afterwards if <code>is_alive()</code> returns true. |
+ | '''"Is there logical xor operator?"''' |
||
− | A useful equivalent of the "ternary operator" from the C language is <code>a and b or c</code>, which evaluates to <code>b</code> if <code>a</code> is true, or <code>c</code> if <code>a</code> is false. |
||
+ | If your operands are ''guaranteed'' to be boolean values, i.e. ''only'' true or false, then the <code>==</code> operator does what an xor operator does. If they are ''not'' guaranteed, then an ugly-but-functional alternative is <code>not b == not c</code>. |
||
⚫ | |||
+ | '''"Is there a ternary operator?"''' |
||
⚫ | |||
+ | |||
+ | A useful equivalent of the choice-making [[wikipedia:?:|<code>?:</code> "ternary operator"]] from the C-family languages is <code>a and b or c</code>, which evaluates to <code>b</code> if <code>a</code> is true, or <code>c</code> if <code>a</code> is false. One important and useful aspect of a ternary operator is that it uses the short-circuiting feature of logical operators to avoid invoking unwanted side effects or costs. '''There is one caveat:''' if <code>b</code> is false or nil, the expression will fall through and choose <code>c</code>. |
||
+ | |||
+ | Much more detail and additional approaches can be found in [http://lua-users.org/wiki/TernaryOperator the official Lua documentation]. Among them is the suggestion to use a ternary ''function'', and it is good to note here that a ternary function uses the same number of PICO-8 tokens per invocation as the logical-operator approach, while also removing the need for isolating complex expressions with parentheses, but with the downsides that short-circuiting is lost and performance is a bit poorer. |
||
⚫ | |||
+ | |||
⚫ | |||
<code>a</code> must be a string. <code>b</code> can be a string or a number (which is coerced into a string). |
<code>a</code> must be a string. <code>b</code> can be a string or a number (which is coerced into a string). |
||
− | No other type of value can be concatenated with a string. You can use the [[Tostr|tostr()]] function to convert other values to strings: |
+ | No other type of value can be concatenated with a string. You can use the [[Tostr|<code>tostr()</code>]] function to convert other values to strings: |
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
print("a > b: "..tostr(a > b)) |
print("a > b: "..tostr(a > b)) |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
⚫ | |||
+ | <syntaxhighlight lang="lua"> |
||
⚫ | |||
x = "hello there" |
x = "hello there" |
||
print(#x) -- 11 |
print(#x) -- 11 |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | See also [[Sub|sub()]]. |
+ | See also [[Sub|<code>sub()</code>]]. |
== Assignment operators == |
== Assignment operators == |
||
Line 105: | Line 118: | ||
<code>myvar = value</code> |
<code>myvar = value</code> |
||
− | + | PICO-8 adds shortcut assignment operators for the arithmetic operators, often found in other languages like C: |
|
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
myvar += value -- myvar = myvar + value |
myvar += value -- myvar = myvar + value |
||
myvar -= value -- myvar = myvar - value |
myvar -= value -- myvar = myvar - value |
||
Line 113: | Line 126: | ||
myvar /= value -- myvar = myvar / value |
myvar /= value -- myvar = myvar / value |
||
myvar %= value -- myvar = myvar % value |
myvar %= value -- myvar = myvar % value |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | (There |
+ | (There are no <code>^=</code> or <code>..=</code> assignment operators.) |
== Variables == |
== Variables == |
||
− | + | PICO-8 supports global variables accessible to the entire program, and local variables accessible only within the function where they are declared. If a variable is not declared as local, then it is global. |
|
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
-- a global variable |
-- a global variable |
||
player_pos = {20, 60} |
player_pos = {20, 60} |
||
Line 134: | Line 147: | ||
return 2 * pi * r |
return 2 * pi * r |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
'''Caution:''' A mistyped variable name is often interpreted as a global variable with no value assigned, which evaluates to <code>nil</code>. Most uses of unexpectedly <code>nil</code> values result in a runtime error, but these are not always easy to find. |
'''Caution:''' A mistyped variable name is often interpreted as a global variable with no value assigned, which evaluates to <code>nil</code>. Most uses of unexpectedly <code>nil</code> values result in a runtime error, but these are not always easy to find. |
||
+ | |||
+ | Note that, unlike vanilla Lua, there is no access to the hidden <code>_G</code> table containing all global values. |
||
+ | |||
+ | However, the table of all global values can still be accessed via the <code>_ENV</code> variable. (Be warned - this is undocumented, though an integral part of the lua language. It can also only be typed in via an external editor due to the caps). |
||
== Functions == |
== Functions == |
||
Line 142: | Line 159: | ||
A function is a collection of statements that can be executed by calling it. The behavior of a function can be parameterized, and a function may return a value as a result. |
A function is a collection of statements that can be executed by calling it. The behavior of a function can be parameterized, and a function may return a value as a result. |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
function distance(x1, y1, x2, y2) |
function distance(x1, y1, x2, y2) |
||
return sqrt((x2 - x1)^2 + (y2 - y1)^2) |
return sqrt((x2 - x1)^2 + (y2 - y1)^2) |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | + | PICO-8 includes many built-in global functions, such as <code>sqrt()</code> in this example. See [[APIReference]]. |
|
If control reaches a <code>return</code> statement, then the function exits. If <code>return</code> includes a value, then the function call evaluates to that value. If control reaches the end of the function's statement block without seeing a <code>return</code> statement, then the function returns <code>nil</code>. |
If control reaches a <code>return</code> statement, then the function exits. If <code>return</code> includes a value, then the function call evaluates to that value. If control reaches the end of the function's statement block without seeing a <code>return</code> statement, then the function returns <code>nil</code>. |
||
Line 156: | Line 173: | ||
A function can omit the name if it is called right away or otherwise used as a value (an "anonymous function"). |
A function can omit the name if it is called right away or otherwise used as a value (an "anonymous function"). |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
x = {2, 3, 5, 7, 11} |
x = {2, 3, 5, 7, 11} |
||
foreach(x, function(v) print(x^2) end) |
foreach(x, function(v) print(x^2) end) |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
Parentheses can be omitted when there is only one argument and it is a table or a literal string: |
Parentheses can be omitted when there is only one argument and it is a table or a literal string: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
add_particle{type=snow, x=rnd(128), y=0, c=7} |
add_particle{type=snow, x=rnd(128), y=0, c=7} |
||
print"hello, winter!" |
print"hello, winter!" |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
Lua functions are [https://en.wikipedia.org/wiki/Scope_(computer_science) lexically scoped]. |
Lua functions are [https://en.wikipedia.org/wiki/Scope_(computer_science) lexically scoped]. |
||
Line 174: | Line 191: | ||
The Lua <code>if</code> statement takes a condition and a block of statements, and executes the statements only if the condition is true: |
The Lua <code>if</code> statement takes a condition and a block of statements, and executes the statements only if the condition is true: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
if score >= 1000 then |
if score >= 1000 then |
||
print("you win!") |
print("you win!") |
||
score = 0 |
score = 0 |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
An <code>if</code> statement can include any number of <code>elseif</code> sections to test multiple conditions until one is found true, and an optional final <code>else</code> section to evaluate if none of the conditions were true. The general form is: |
An <code>if</code> statement can include any number of <code>elseif</code> sections to test multiple conditions until one is found true, and an optional final <code>else</code> section to evaluate if none of the conditions were true. The general form is: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
if cond1 then |
if cond1 then |
||
... |
... |
||
Line 191: | Line 208: | ||
... |
... |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | + | PICO-8 extends standard Lua with an abbreviated, single-line <code>if</code> statement, differentiated by having parentheses around the condition and no <code>then</code> or <code>end</code> keywords. You may use <code>else</code>, but it must be on the same line. There is no support for <code>elseif</code>. Some examples: |
|
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
if (cond1) print("cond1") |
if (cond1) print("cond1") |
||
if (cond2) print("cond2") else print("not cond2") |
if (cond2) print("cond2") else print("not cond2") |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
A common misconception is that you must compare a boolean variable to <code>true</code> or <code>false</code> in an <code>if</code> statement, but a condition ''is'' a boolean, so you can drop any boolean variable straight into an <code>if</code> statement: |
A common misconception is that you must compare a boolean variable to <code>true</code> or <code>false</code> in an <code>if</code> statement, but a condition ''is'' a boolean, so you can drop any boolean variable straight into an <code>if</code> statement: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
-- these two blocks are functionally identical |
-- these two blocks are functionally identical |
||
Line 217: | Line 234: | ||
print("not cond") |
print("not cond") |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | The only time you might need to compare a boolean variable to a value is when it may not be initialized yet. When variables are uninitialized, their value will be <code>nil</code>. |
+ | The only time you might need to compare a boolean variable to a value is when it may not be initialized yet. When variables are uninitialized, their value will be <code>nil</code>. When a <code>nil</code> value is used as a conditional expression, it is treated the same as <code>false</code>. Thus, if you have a boolean variable that may not be set yet, you should check for <code>nil</code> and initialize it before testing it as a boolean. |
== while and repeat loops == |
== while and repeat loops == |
||
Line 225: | Line 242: | ||
The <code>while</code> statement executes a block of statements repeatedly as long as a given conditional expression is true: |
The <code>while</code> statement executes a block of statements repeatedly as long as a given conditional expression is true: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
x = 0 |
x = 0 |
||
while x < 5 do |
while x < 5 do |
||
Line 231: | Line 248: | ||
x += 1 |
x += 1 |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
The <code>repeat</code> statement executes a block of statements repeatedly until a given conditional expression is true: |
The <code>repeat</code> statement executes a block of statements repeatedly until a given conditional expression is true: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
x = 0 |
x = 0 |
||
repeat |
repeat |
||
Line 241: | Line 258: | ||
x += 1 |
x += 1 |
||
until x > 4 |
until x > 4 |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
<code>while</code> does not execute its block if the condition is already false. <code>repeat</code> always executes its block at least once, then tests the condition. |
<code>while</code> does not execute its block if the condition is already false. <code>repeat</code> always executes its block at least once, then tests the condition. |
||
Line 251: | Line 268: | ||
There are two kinds of for loops in Lua. The numeric for loop traverses a numeric sequence from start to end, using an optional "stride" (with a default stride of 1). |
There are two kinds of for loops in Lua. The numeric for loop traverses a numeric sequence from start to end, using an optional "stride" (with a default stride of 1). |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
-- draws 16 color bars |
-- draws 16 color bars |
||
for c=0,15 do |
for c=0,15 do |
||
Line 261: | Line 278: | ||
print(i) |
print(i) |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | The other kind of for loop is the "generic for." In |
+ | The other kind of for loop is the "generic for." In PICO-8, this is most commonly used with the [[All|<code>all()</code>]] and [[Pairs|<code>pairs()</code>]] built-ins for traversing tables: |
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
tbl = {2, 3, 5, 7, 11} |
tbl = {2, 3, 5, 7, 11} |
||
for v in all(tbl) do |
for v in all(tbl) do |
||
Line 275: | Line 292: | ||
print(k.."="..v) |
print(k.."="..v) |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | The generic for statement expects the "in" expression to return a special kind of value called an ''iterator''. The built-ins [[All|all()]] and [[Pairs|pairs()]] return iterators. For information on how to create your own iterators, see [https://www.lua.org/pil/7.html Iterators and the Generic for] in the Lua manual. |
+ | The generic for statement expects the "in" expression to return a special kind of value called an ''iterator''. The built-ins [[All|<code>all()</code>]] and [[Pairs|<code>pairs()</code>]] return iterators. For information on how to create your own iterators, see [https://www.lua.org/pil/7.html Iterators and the Generic for] in the Lua manual. |
As with <code>while</code> and <code>repeat</code>, you can use the <code>break</code> statement to terminate a for loop immediately. |
As with <code>while</code> and <code>repeat</code>, you can use the <code>break</code> statement to terminate a for loop immediately. |
||
Line 285: | Line 302: | ||
Tables are the primary composite data structure in Lua. They are used as containers, especially sequences (like lists or arrays) and mappings (also known as dictionaries or hashtables). Tables can be used as general purpose objects, and when combined with metatables (see below) can implement object-oriented concepts such as inheritance. |
Tables are the primary composite data structure in Lua. They are used as containers, especially sequences (like lists or arrays) and mappings (also known as dictionaries or hashtables). Tables can be used as general purpose objects, and when combined with metatables (see below) can implement object-oriented concepts such as inheritance. |
||
− | See [[Tables]] for a complete introduction to using tables in |
+ | See [[Tables]] for a complete introduction to using tables in PICO-8. |
== Methods == |
== Methods == |
||
A method is a function that is stored as a value in a table. Lua has special syntax for defining and calling methods that cause the table itself to be passed to the method as a parameter named <code>self</code>. |
A method is a function that is stored as a value in a table. Lua has special syntax for defining and calling methods that cause the table itself to be passed to the method as a parameter named <code>self</code>. |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
ball = { |
ball = { |
||
xpos = 60, |
xpos = 60, |
||
Line 305: | Line 322: | ||
ball:move(100, 120) |
ball:move(100, 120) |
||
print(ball.xpos) -- 100 |
print(ball.xpos) -- 100 |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
In both the definition and the method call, using the colon (<code>:</code>) instead of the dot (<code>.</code>) implies the <code>self</code> behavior. If you define the method as a simple property of the table, you must remember to explicitly mention the <code>self</code> argument. This is equivalent: |
In both the definition and the method call, using the colon (<code>:</code>) instead of the dot (<code>.</code>) implies the <code>self</code> behavior. If you define the method as a simple property of the table, you must remember to explicitly mention the <code>self</code> argument. This is equivalent: |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
ball = { |
ball = { |
||
xpos = 60, |
xpos = 60, |
||
Line 326: | Line 343: | ||
-- using the dot, must pass self explicitly |
-- using the dot, must pass self explicitly |
||
ball.move(ball, 100, 120) |
ball.move(ball, 100, 120) |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
+ | |||
Line 335: | Line 353: | ||
The most common use of metatables is to implement object-oriented inheritance by redefining the <code>__index</code> operator. The new definition tells Lua to check for a given property on a parent prototype object if the property is not set on the current object. |
The most common use of metatables is to implement object-oriented inheritance by redefining the <code>__index</code> operator. The new definition tells Lua to check for a given property on a parent prototype object if the property is not set on the current object. |
||
+ | <syntaxhighlight lang="lua"> |
||
− | <pre> |
||
function myclass:new(o) |
function myclass:new(o) |
||
o = o or {} |
o = o or {} |
||
Line 342: | Line 360: | ||
return o |
return o |
||
end |
end |
||
+ | </syntaxhighlight> |
||
− | </pre> |
||
− | + | PICO-8 supports the [[Setmetatable|<code>setmetatable()</code>]], [[Getmetatable|<code>getmetatable()</code>]], [[Rawset|<code>rawset()</code>]], [[Rawget|<code>rawget()</code>]], [[Rawlen|<code>rawlen()</code>]], and <code>[[Rawequal|rawequals()]]</code> built-ins. It does not support any other related Lua functions. |
|
− | See [[Setmetatable|setmetatable()]] for more information, links to references, and a more complete example. |
+ | See [[Setmetatable|<code>setmetatable()</code>]] for more information, links to references, and a more complete example. |
== Coroutines == |
== Coroutines == |
||
Line 352: | Line 370: | ||
A coroutine is a special kind of function that can yield control back to the caller without completely exiting. The caller can then resume the coroutine as many times as needed until the function exits. |
A coroutine is a special kind of function that can yield control back to the caller without completely exiting. The caller can then resume the coroutine as many times as needed until the function exits. |
||
− | + | PICO-8 supports coroutines using built-in global functions instead of Lua's <code>coroutine</code> library. See [[Cocreate|<code>cocreate()</code>]], [[Coresume|<code>coresume()</code>]], [[Costatus|<code>costatus()</code>]], and [[Yield|<code>yield()</code>]]. |
|
− | See [[Cocreate|cocreate()]] for more information, links to references, and a complete example. |
+ | See [[Cocreate|<code>cocreate()</code>]] for more information, links to references, and a complete example. |
= Differences from Lua = |
= Differences from Lua = |
||
− | + | PICO-8 does not include the Lua standard library. See [[Math]] for a description of the PICO-8 mathematical functions. See [[APIReference]] for a complete list of PICO-8 built-in functions. |
|
− | josefnpat wrote a list of technical differences between |
+ | josefnpat wrote a list of technical differences between PICO-8's Lua and the official Lua 5.2. |
* List of differences: https://gist.github.com/josefnpat/bfe4aaa5bbb44f572cd0 |
* List of differences: https://gist.github.com/josefnpat/bfe4aaa5bbb44f572cd0 |
||
* Discussion: http://www.lexaloffle.com/bbs/?tid=2611 |
* Discussion: http://www.lexaloffle.com/bbs/?tid=2611 |
||
+ | [[Category:Reference]] |
||
+ | [[Category:Lua]] |
Revision as of 07:21, 24 February 2020
Lua is a programming language designed primarily for embedded systems. It is popular in the video game industry as a language that can be embedded in a larger game engine.
PICO-8 implements a subset of Lua for writing game cartridges. Because it is a subset, not all features of Lua are supported. Most notably, PICO-8 does not include the Lua standard library, and instead provides a proprietary collection of global functions.
Features of PICO-8 Lua
The following are the major language features of Lua as implemented by PICO-8.
Comments
PICO-8 code comments are preceded by two hyphens (--
) and go to the end of the line. They also support a multi-line syntax using double-brackets (--[[ ... ]]
).
-- one-line comment
--[[ multi-line
comment ]]
Values
PICO-8 supports these fundamental types of values:
- Numbers, in the range supported by signed 16.16 bit fixed point:
- Minimum value: hex
0x8000.0000
, decimal-32768.0
- Maximum value: hex
0x7fff.ffff
, decimal32767.9999847412109375
- Number literals can use decimal (
17.25
) or hexadecimal (0x11.4
). - Things to be aware of:
- PICO-8 prints and converts numbers to decimal strings by rounding off to four decimal places. This has the side effect that the maximum number rounds up to
32768.0
, which is actually not a valid PICO-8 number. It also means that the tiniest negative fraction below 0 displays as the somewhat-unintuitive-0
. - This range is narrow in comparison to modern platforms. Be aware of situations where you might overflow, e.g. calculating the distance between corners of the screen requires calculating 128*128+128*128, which will overflow and wrap to -32768.
- PICO-8 prints and converts numbers to decimal strings by rounding off to four decimal places. This has the side effect that the maximum number rounds up to
- Minimum value: hex
- Booleans (
true
andfalse
) - Strings
- String literals can use single quotes (
'hello'
) or double quotes ("hello"
), and the quote character can appear in the string escaped with a leading backslash ("you said \"hello\", yes?"
).
- String literals can use single quotes (
- Nil (
nil
) - Functions (see below)
- Coroutines (see below)
- Tables
- Sequences, indexed from 1:
x = {2, 3, 5, 7, 11}
;x[1] == 2
- Mappings:
x = {a=1, b=2, c=3}
;x.a == x['a'] == 1
- Unset indexes evaluate to
nil
:x[0] == nil
- Sequences, indexed from 1:
As in Lua, all composite and custom data types are based on tables.
Arithmetic operators
PICO-8 supports these arithmetic operators:
- addition:
a + b
- subtraction:
a - b
- multiplication:
a * b
- division:
a / b
- modulo:
a % b
- exponentiation:
a ^ b
Arithmetic operators take numbers as arguments and return a number.
Relational operators
PICO-8 supports these relational operators:
- less than:
a < b
- greater than:
a > b
- less than or equal to:
a <= b
- greater than or equal to:
a >= b
- equal:
a == b
- not equal:
a ~= b
; PICO-8 synonym:a != b
Relational operators take numbers as arguments and return either true
or false
.
Logical operators
PICO-8 supports the following logical operators:
- and:
a and b
- or:
a or b
- not:
not a
In logical expressions, both false
and nil
are treated as false, and all other values are treated as true.
Logical expressions "short circuit," which is to say they stop evaluating expressions left to right as soon as the value of the expression is known. For example, "is_alive() and can_shoot()"
will first call is_alive()
, and will only call can_shoot()
afterwards if is_alive()
returns true.
"Is there logical xor operator?"
If your operands are guaranteed to be boolean values, i.e. only true or false, then the ==
operator does what an xor operator does. If they are not guaranteed, then an ugly-but-functional alternative is not b == not c
.
"Is there a ternary operator?"
A useful equivalent of the choice-making ?:
"ternary operator" from the C-family languages is a and b or c
, which evaluates to b
if a
is true, or c
if a
is false. One important and useful aspect of a ternary operator is that it uses the short-circuiting feature of logical operators to avoid invoking unwanted side effects or costs. There is one caveat: if b
is false or nil, the expression will fall through and choose c
.
Much more detail and additional approaches can be found in the official Lua documentation. Among them is the suggestion to use a ternary function, and it is good to note here that a ternary function uses the same number of PICO-8 tokens per invocation as the logical-operator approach, while also removing the need for isolating complex expressions with parentheses, but with the downsides that short-circuiting is lost and performance is a bit poorer.
String operators
PICO-8 supports the string concatenation operator:
a..b
a
must be a string. b
can be a string or a number (which is coerced into a string).
No other type of value can be concatenated with a string. You can use the
tostr()
function to convert other values to strings:
print("a > b: "..tostr(a > b))
You can determine the length of a string using the sequence length operator (#):
x = "hello there"
print(#x) -- 11
See also sub()
.
Assignment operators
As in Lua, you can assign a value to a variable or table slot
myvar = value
PICO-8 adds shortcut assignment operators for the arithmetic operators, often found in other languages like C:
myvar += value -- myvar = myvar + value
myvar -= value -- myvar = myvar - value
myvar *= value -- myvar = myvar * value
myvar /= value -- myvar = myvar / value
myvar %= value -- myvar = myvar % value
(There are no ^=
or ..=
assignment operators.)
Variables
PICO-8 supports global variables accessible to the entire program, and local variables accessible only within the function where they are declared. If a variable is not declared as local, then it is global.
-- a global variable
player_pos = {20, 60}
function move_player(newx, newy)
player_pos = {newx, newy}
end
function circumference(r)
-- a local variable
local pi = 3.14
return 2 * pi * r
end
Caution: A mistyped variable name is often interpreted as a global variable with no value assigned, which evaluates to nil
. Most uses of unexpectedly nil
values result in a runtime error, but these are not always easy to find.
Note that, unlike vanilla Lua, there is no access to the hidden _G
table containing all global values.
However, the table of all global values can still be accessed via the _ENV
variable. (Be warned - this is undocumented, though an integral part of the lua language. It can also only be typed in via an external editor due to the caps).
Functions
A function is a collection of statements that can be executed by calling it. The behavior of a function can be parameterized, and a function may return a value as a result.
function distance(x1, y1, x2, y2)
return sqrt((x2 - x1)^2 + (y2 - y1)^2)
end
PICO-8 includes many built-in global functions, such as sqrt()
in this example. See APIReference.
If control reaches a return
statement, then the function exits. If return
includes a value, then the function call evaluates to that value. If control reaches the end of the function's statement block without seeing a return
statement, then the function returns nil
.
A function in Lua is a "first class" value, just like other values. A named function in the outermost block is equivalent to a global variable whose value is a function. A named function can also appear inside another function, which is equivalent to a local variable.
A function can omit the name if it is called right away or otherwise used as a value (an "anonymous function").
x = {2, 3, 5, 7, 11}
foreach(x, function(v) print(x^2) end)
Parentheses can be omitted when there is only one argument and it is a table or a literal string:
add_particle{type=snow, x=rnd(128), y=0, c=7}
print"hello, winter!"
Lua functions are lexically scoped.
Conditional statements
The Lua if
statement takes a condition and a block of statements, and executes the statements only if the condition is true:
if score >= 1000 then
print("you win!")
score = 0
end
An if
statement can include any number of elseif
sections to test multiple conditions until one is found true, and an optional final else
section to evaluate if none of the conditions were true. The general form is:
if cond1 then
...
elseif cond2 then
...
else
...
end
PICO-8 extends standard Lua with an abbreviated, single-line if
statement, differentiated by having parentheses around the condition and no then
or end
keywords. You may use else
, but it must be on the same line. There is no support for elseif
. Some examples:
if (cond1) print("cond1")
if (cond2) print("cond2") else print("not cond2")
A common misconception is that you must compare a boolean variable to true
or false
in an if
statement, but a condition is a boolean, so you can drop any boolean variable straight into an if
statement:
-- these two blocks are functionally identical
if cond == true then
print("cond")
elseif cond == false then
print("not cond")
end
if cond then
print("cond")
else
print("not cond")
end
The only time you might need to compare a boolean variable to a value is when it may not be initialized yet. When variables are uninitialized, their value will be nil
. When a nil
value is used as a conditional expression, it is treated the same as false
. Thus, if you have a boolean variable that may not be set yet, you should check for nil
and initialize it before testing it as a boolean.
while and repeat loops
The while
statement executes a block of statements repeatedly as long as a given conditional expression is true:
x = 0
while x < 5 do
print(x)
x += 1
end
The repeat
statement executes a block of statements repeatedly until a given conditional expression is true:
x = 0
repeat
print(x)
x += 1
until x > 4
while
does not execute its block if the condition is already false. repeat
always executes its block at least once, then tests the condition.
The break
statement anywhere in a loop terminates the loop immediately without testing the condition. If the loop is nested inside another loop, only the innermost loop is terminated.
for loops
There are two kinds of for loops in Lua. The numeric for loop traverses a numeric sequence from start to end, using an optional "stride" (with a default stride of 1).
-- draws 16 color bars
for c=0,15 do
rectfill(c*8, 0, c*8+7, 127, c)
end
-- prints 1, 3, 5, 7, 9
for i=1,10,2 do
print(i)
end
The other kind of for loop is the "generic for." In PICO-8, this is most commonly used with the all()
and pairs()
built-ins for traversing tables:
tbl = {2, 3, 5, 7, 11}
for v in all(tbl) do
print(v)
end
tbl = {a=1, b=2, c=3}
for k,v in pairs(tbl) do
print(k.."="..v)
end
The generic for statement expects the "in" expression to return a special kind of value called an iterator. The built-ins all()
and pairs()
return iterators. For information on how to create your own iterators, see Iterators and the Generic for in the Lua manual.
As with while
and repeat
, you can use the break
statement to terminate a for loop immediately.
Tables
Tables are the primary composite data structure in Lua. They are used as containers, especially sequences (like lists or arrays) and mappings (also known as dictionaries or hashtables). Tables can be used as general purpose objects, and when combined with metatables (see below) can implement object-oriented concepts such as inheritance.
See Tables for a complete introduction to using tables in PICO-8.
Methods
A method is a function that is stored as a value in a table. Lua has special syntax for defining and calling methods that cause the table itself to be passed to the method as a parameter named self
.
ball = {
xpos = 60,
ypos = 60
}
function ball:move(newx, newy)
self.xpos = newx
self.ypos = newy
end
print(ball.xpos) -- 60
ball:move(100, 120)
print(ball.xpos) -- 100
In both the definition and the method call, using the colon (:
) instead of the dot (.
) implies the self
behavior. If you define the method as a simple property of the table, you must remember to explicitly mention the self
argument. This is equivalent:
ball = {
xpos = 60,
ypos = 60,
-- without the colon syntax, must mention self argument explicitly
move = function(self, newx, newy)
self.xpos = newx
self.ypos = newy
end
}
-- using the colon, ball is passed as self automatically
ball:move(100, 120)
-- using the dot, must pass self explicitly
ball.move(ball, 100, 120)
Metatables
The metatable for a table defines the behavior of using the table as a value with Lua operators. You can customize a table's metatable to specify custom operator behaviors.
The most common use of metatables is to implement object-oriented inheritance by redefining the __index
operator. The new definition tells Lua to check for a given property on a parent prototype object if the property is not set on the current object.
function myclass:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
PICO-8 supports the setmetatable()
, getmetatable()
, rawset()
, rawget()
, rawlen()
, and rawequals()
built-ins. It does not support any other related Lua functions.
See setmetatable()
for more information, links to references, and a more complete example.
Coroutines
A coroutine is a special kind of function that can yield control back to the caller without completely exiting. The caller can then resume the coroutine as many times as needed until the function exits.
PICO-8 supports coroutines using built-in global functions instead of Lua's coroutine
library. See cocreate()
, coresume()
, costatus()
, and yield()
.
See cocreate()
for more information, links to references, and a complete example.
Differences from Lua
PICO-8 does not include the Lua standard library. See Math for a description of the PICO-8 mathematical functions. See APIReference for a complete list of PICO-8 built-in functions.
josefnpat wrote a list of technical differences between PICO-8's Lua and the official Lua 5.2.
- List of differences: https://gist.github.com/josefnpat/bfe4aaa5bbb44f572cd0
- Discussion: http://www.lexaloffle.com/bbs/?tid=2611