User:Slyme/Discards/Lua Basics: Difference between revisions

From FiguraMC
Slyme (talk | contribs)
Final edit before discarding.
Slyme (talk | contribs)
m Slyme moved page User:Slyme/Drafts/Lua Basics to User:Slyme/Discards/Lua Basics: Discarded draft
(No difference)

Revision as of 01:27, 7 October 2024

This page is heavily unfinished.
For the time being, please visit the real Lua Basics page.

The Interpreter

The interpreter is just the thing that runs your Lua code. In Figura's case, it's a custom version of LuaJ, an interpreter written in Java.

A Lua interpreter reads each line sequentially, executing each line before moving on to the next one. This makes it hard to break away from the sequence without breaking the program entirely for something like a sleep call.

Comments

Comments are used to tell the interpreter to ignore a part of the code, either because it's for human programmers to see or it's a piece of code that should not be executed.

Short comments, comments that only affect one line, start with --. From the two dashes to the end of the line, code will be ignored.

--This is a comment.
local x = 2 -- Everything after the two dashes will be ignored.
-- local y = 3 -- The preceding code will not run because it's part of a comment.
-- This is useful in case you need to document your code for future programmers or you need to stop code from running without removing it from the script.

Long Comments

Long comments, comments that can go over multiple lines, come in the form of --[[ ]]. Everything between the two brackets is commented and, thus, ignored by the interpreter.

--[[
  This is a block comment. None of the
  lines within the comment will be executed
  until the final bracket.
--]] -- Sometimes programmers will put two dashes before the end of block comments. This is not required but looks nice.
local z = 5 -- You can use both types of comments within the same script.
--[[
  Block comments are useful for long
  explanations of code or for ignoring
  large swaths of code.
  ]]

Variables

Think of variables as a bunch of boxes. Each variable is one box, a label printed on it with an object (or, in some cases, a few) stored inside.

To put something inside a variable, you'll want to set it equal to that something. For example...

local a = 247 -- Make a new local variable "a" and set  the value within to be the number 247.
local b = "Hello, World!" -- Make a new local variable "b" and set the value within to be the string "Hello, World!"
c = 10 -- Make a new *global* variable "c" and set the value within to be the number 10.

The last variable, c, is a global variable. This means that it can be used anywhere within the Lua environment. While this may seem like a good thing at first, there are many cases where this may not be a thing you want. For example, let's see how a system like below, assuming each script is run in sequence in the same environment, would fare with global variables

test-script-1.lua
script = 1
return function() return script end
test-script-2.lua
script = 2
return function() return script end
test-script-3.lua
script = 3
return function() return script end
script-checker.lua
print(require("test-script-1")()) --> 3
print(require("test-script-2")()) --> 3
print(require("test-script-3")()) --> 3

We'll get more into variable scopes later. Watch out for all other instances of c throughout the rest of the document, it may interfere with other scripts. ;)

Valid Names

Variable names...

  • Can only use alphanumeric characters and _
  • Must not start with a number.
  • Cannot be the same as a Lua keyword (and, or, if, while, etc.)

For instance...

local a -- ✅ Valid
local _b -- ✅ Valid
local aBc123 -- ✅ Valid
local 123abc -- ❌ Invalid; Variable names cannot start with a number.
local foo-bar -- ❌ Invalid; Variable names can only use alphanumeric characters.
local and -- ❌ Invalid; Variable names cannot be the same as a Lua keyword.
local local -- ❌ Invalid; Variable names cannot be the same as a Lua keyword.

Declaring Without Setting

In some cases, you may want to declare a local variable without setting its value. This can be done by just writing local variableName without adding a =

local a -- a is now initialized as a local variable with no value.
print(a) --> nil
a = 3 -- Variables already declared as locals do not need to be marked as local again.
print(a) --> 3

Types

A type is any sort of object, whether it be a few words, a number, a true/false value, etc.

boolean

Booleans hold one of two values: true or false.

These are typically returned when a relational operator (==, >, <=, etc.) or a boolean operator (not, and, etc.) is used.

print(true) --> true
print(5 > 3) --> true
print(10 <= 6) --> false
print(true and false) --> false

number

Numbers are numbers, either integer or decimal.

Numbers are typically returned when an arithmetic operator (+, -, *, /, etc.) is used.

print(5) --> 5
print(5 + 2) --> 7
print(5 * 2) --> 10
print(5 / 2) --> 2.5

string

Strings are sequences of characters, most notably text. When defined in code, strings must be wrapped in either quotes (") or single quotes (').

Strings can be modified using the String standard library, accessible using either the string global or by using the methods on strings themselves.

When defining a string in code, make sure to use \n in place of linebreaks within the string. Using real line breaks instead of \n will result in a syntax error.

You may also need to escape single/double quotes in case the character you want to use is already used to wrap the string. In that case, just put a backslash (\) in front of the character to escape it (for example, "\"" is the same as '"').

Literal Strings

Strings, when surrounded by double brackets ([[]]), take characters at face value. These are called literal strings and can be used to make a string span multiple lines.

print("Hello, World!") --> Hello, World!
print("\"Line 1\"
'Line 2'") -- ERROR! unfinished string near '""Line 1"'
print("\"Line 1\"\n'Line 2'") --> "Line 1"
                              --  'Line 2'
print([["Line 1"
'Line 2']]) --> "Line 1"
            --  'Line 2'

nil

Unlike other types, nil is the absence of a value. This is what's returned when you try to use a variable not defined yet.

print(a) --> nil
print(b) --> nil
print(c) --> 10
-- Oh, there's our lovely little global "c" again! Didn't expect that, did you?

Scripts can at best return/display unwanted values and at worst error if a nil pops up where it shouldn't.

string = nil
local a = "Hello, World!"
print(string.sub(a, 2, 5)) -- ERROR! attempt to index a nil value (global 'string')

table

Tables are where things start to get more complex. Instead of containing one object, tables can store many objects in multiple different ways.

For instance, a table can store a sequence of objects. This is commonly called an array. Arrays start at an index of one and continue counting up for each item added to the table.

However, you don't just have to use numbers as indices. You can use any basic object (boolean, number, string, nil) you want! Doing so makes what's commonly called a dictionary, the indices called keys and the values called values.

local a = {"Hello,", "World!"}
print(a[1]) --> Hello,
a["diamond"] = true
print(a.diamond) --> true -- Doing something like a.diamond is the same as doing a["diamond"]
local b = {
    [c] = true, -- I hope you remember what c is!
    [true] = 56,
    [5<3] = b or c -- Expressions can be used as keys and/or values. In this case, (5<3) comes out to true and (c and d) comes out to 10 since d is nil and global c is still 10.
    ["foo"] = "bar"
    xyz = "abc" -- String keys can be removed from their brackets and quotes as long as they follow the variable name rules above.
}
print(b[b.foo == "bar"]) --> 56 -- Expressions can also be used to index a table.

Sometimes, you may want to go over all items within a table. This can be done with a for loop.