Lua

File size
14.6KB
Lines of code
397

Lua

Comments

-- single-line comment

--[[ 
this is 
a 
multi-line
comment
]]--

Printing

-- ---------- PRINTING ----------
    -- print() appends a newline to the output by default
    -- io.write() does not append a newline to the output
    -- io.read() takes in user input from the stdin
    -- .. for string concatenation

print("this is just like python, it comes with a newline")
io.write("this also works, defaults to the standard out\n")

watermelon = io.read("Waiting for your input lah: ") -- assigns user input to the string variable watermelon

stringOne = "Hello "
stringTwo = "World!"

print(stringOne .. stringTwo) -- this prints "Hello World!" to the console

Types

-- ---------- VARIABLE ----------
    -- variables are global by default
    -- local makes them local

iAmGlobal = "Gojo Satoru"
local iAmLocal = "Geto Suguru"

-- ---------- TYPE ----------
    -- number (integers, floats, doubles)
    -- string (single, double-quoted, [[]] square brackets; strings are immutable like python)
    -- boolean (true, false; only nil and false evaluate to false, everything else evalautes to true including 0 and '')
    -- nil (no data; undefined variables evalaute to nil, no error is hit)

aNumber = 42
anotherNumber = 12.890
aString = "watermelon"
aMultilineString = [[ Double brackets for 
                      multi-line
                      string ]]
aBoolean = true
aNil = nil
foo = anUndefinedVariable -- this evaluates to nil and the program continues running

-- TYPE CONVERSION 

tostring() 
tonumber()
.. -- string concatenation have implicit type conversion from number to string, though for greater readability we try to avoid this as far as possible
+ - * / -- arithmetic operations have implicit type conversion from boolean to number, true => 1 and false => 0, though for greater readability we try to avoid implicit type conversions as far as possible

Operators

-- ---------- OPERATOR ----------

-- ARITHMETIC OPERATOR

+ -- addition
- -- subtraction
* -- multiplication
/ -- divison
% -- modulo
^ -- exponentiation (to the power of)

-- RELATIONAL OPERATOR

== -- complete equality check of type and value
~= -- complete inequality check of type and value
< -- comparison operator
> -- comparison operator
<= -- comparison operator
>= -- comparison operator

-- LOGICAL OPERATOR

and -- logical and
or -- logical or
not -- logical not

Control structures

-- ---------- BLOCK -----------
    -- lua takes a page from bash's syntax by seperating its code blocks with do/end and indentation 

-- ---------- IF ELSEIF ELSE THEN ----------
    -- standard conditionals
    -- there are no switch case statements in lua

if num > 40 then 
    print("Over 40")
elseif num == 20 then
    print("It's 20")
else 
    print("Lovely")

-- ---------- LOOP ----------

-- FOR LOOP
    -- for {VARIABLE BEGINNING}, {VARIABLE END}, {STEP}
    -- ranges are inclusive on both ends

for i=10, 1, -1
do 
    print(i) -- prints 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 to the console
end

for q=1,100 do 
    print(q) -- prints the numbers 1 to 100 to the console
end

-- FOR IN LOOP
    -- useful when looping over iterable data structures

for key,value in pairs(aTable) 
do
    aTable[key] = "i am jujutsu kaisen"
end

-- WHILE LOOP
    -- while do loop

while num < 50 do 
    num = num + 1
end

-- REPEAT UNTIL LOOP
    -- basically a do while loop

breakCase = 10
repeat
    print("watermelon")
    breakCase = breakCase - 1
until breakCase == 0

Data structures

-- --------- WARNING ---------
    -- indice count starts at 1 in lua, important for indexing

-- ---------- TABLE ----------
    -- lua's only data structure is the table, similar to PHP's associative arrays (both a dictionary and a list)
    -- initialised using {} curly braces
    -- table keys are string values by default so quotation marks are unnecessary
    -- table keys can also be non-string values, and are declared via literal notation
    -- table values can be accessed via . dot notation or [] square bracket notation
    -- can assign functions to a table, wherein the function name is the key and the function body is the value (using both named function / anonymous function syntax are valid)
    -- table functions are called using the same . dot notation as other table values

aTable = {key1 = "value1", key2 = false}
print(aTable.key1) -- prints the value associated with the key key1 in aTable
aTable.key3 = 200 -- adds a new key value pair to aTable of key key3 and value 200
aTable.key2 = nil -- removes key2 from the table

anotherTable = {["@!#"] = 'qbert', [{}] = 1729, [6.29] = "tau"} -- initialise a table with non-string non-nil keys
print(anotherTable[6.29]) -- prints the string value "tau"

-- ---------- TABLE FUNCTION ----------
    -- note either of the below 2 syntax are valid and achieve the same result

-- NAMED FUNCTION SYNTAX

function anotherTable.aFunction() 
    print("Hello from a function!")
end

anotherTable.aFunction() -- calls the table function, printing "Hello from a function!"

-- ANONYMOUS FUNCTION SYNTAX

anotherTable.anAnonFunction() = function() 
    print("Hello from an anonymous function!")
end

anotherTable.anAnonFunction() -- calls the table function similarly, printing "Hello from an anonymous function!"

-- ---------- TABLE 'LIST' ----------
    -- 'lists' are really just tables with implicit indice keys associated with each value upon initalisation
    -- # for length of table (number of key value pairs)

aListButActuallyATable = {"value1", 'value2', 1.21, true}
print(#aListButActuallyATable) -- # returns the size of the table, this works on strings too
for i = 1, #aListButActuallyATable do  -- recall that indices start at 1 in lua
    print(v[i]) -- this syntax works since we're technically using tables with implicit indice keys 
end

-- ---------- GLOBAL TABLE ----------
    -- every lua program has a special _G global table that stores all global variables and functions (global functionality)
    -- _G is the table name, and it has similar functionality as every other table

aNumber = 42

function aFunction() 
    print("Hello my function!")
end

print(_G.aNumber) -- prints 42
_G.aFunction -- calls the function, printing "Hello my function!"
_G.aNewBoolean = true -- assigns the key aNewBoolean with value true to the global table, though this is not recommended unless explicitly necessary for the sake of producing readable code

-- ---------- METATABLE -----------
    -- metatables are tables that define and customise table behaviour
    -- setmetatable() sets a metamethod on a specified table, creating a metatable

-- ---------- METAMETHOD ----------
    -- functions that define specific operations on tables, lua has a set of predefined metamethods as below
    -- __add => define behaviour when adding 2 tables
    -- __sub => define behaviour when subtracting 2 tables
    -- __mul => define behaviour when multiplying 2 tables
    -- __div => define behaviour when dividing 2 tables
    -- __mod => define behaviour when calling modulo operator on 2 tables
    -- __pow => define behaviour when calling exponentiation operator on 2 tables
    -- __len => define behaviour when calling the # length operator on a table or string
    -- __unm => define behaviour when calling the unary minus operator in a table, used for negating a table
    -- __concat => define behaviour when calling concatenation operator on 2 tables
    -- __call => define behaviour when calling a table as a function
    -- __index => define a fallback method when accessing a table key that no longer exists
    -- __newindex => define behaviour when assigning a value to a table key that no longer exists
    -- __eq, __lt, __le => define behaviour for == equality, < less than and <= less than or equal to operators
    -- __tostring => define behaviour when converting a table to a string

-- attempting to simulate behaviour of fractions via tables
numeratorTable = {a = 1, b = 2}
denominatorTable = {a = 2, b = 3}

metaFraction = {} -- metaFraction is just another table for now

function metaFraction.__add(numeratorTable, denominatorTable) -- __add is the metamethod called on the metaFraction table to define behaviour when adding 2 tables together
    sum = {}
    sum.b = numeratorTable.b * denominatorTable.b
    sum.a = numeratorTable.a * denominatorTable.b + denominatorTable.a * numeratorTable.b
    return sum
end

setmetatable(numeratorTable, metaFraction) -- by pairing the table on which the metamethod __add is called with the 2 other tables, we prescribe behaviour and thus crete metatables using the setmetatable() function
setmetatable(denominatorTable, metaFraction)

newSum = numeratorTable + denominatorTable -- this now calls __add(numeratorTable, denominatorTable) on numeratorTable's metatable

-- ---------- CLASS-LIKE TABLE ----------
    -- lua has no built-in classes, though OOP behaviour can be simulated with tables and metatables
    -- function {TABLE NAME}:{FUNCTION NAME}() is the same as function {TABLE NAME}.{FUNCTION NAME}(self), the : just adds the self as the first argument 
    -- self works as you would expect it to in conventional OOP, so here aDog is an object and Dog is the class

Dog = {} -- Dog might act like a class, but remember its just a table with a metatable and metamethods prescribed on it

function Dog.new(self)
    newObject = {sound = "Woof"}
    self.__index = self
    return setmetatable(newObject, self)
end

function Dog.makeSound()
    print("I say" .. self.sound)
end

aDog = Dog:new()
aDog:makeSound() -- "I say Woof"

-- ---------- INHERITANCE ----------
    -- inheritance can be simulated by creating a new table off the original table and assigning new methods to that table, which is then used as a template for further tables
    -- since we're effectively copying keys and values off the original table, the new table template 'inherits' the keys values and methods off the old table which aren't overriden
    -- remember that everything here is a table

loudDog = Dog:new()

function loudDog.makesound()
    sound = self.sound .. " "
    print(sound .. sound .. sound)
end

aSuperLoudDog = loudDog:new()
aSuperLoudDog:makesound() -- "Woof Woof Woof "

Functions

-- ---------- FUNCTIONS ----------
    -- function/end declares a function block
    -- return works as expected
    -- closures and anonymous functions are allowed
    -- functions are first-class and can be fed into other functions
    -- fuction scope can be specified using the local/global keyword

function fib(n)
    if n < 2 then 
        return 1 
    end 
    return fib(n-2) + fib(n-1)
end

function adder(x)
    return function (y) return x + y end -- returns an anonymous function which retains the value of x
end

Modules

-- ---------- MODULE ----------
    -- require() allows for global functionality from the specified file to be brought into global scope of current file
        -- require()'s return values are cached so a file is only run ONCE even if require() is run multiple times
    -- dofile() is like require() but without caching, so a file is run EVERYTIME dofile() is run
    -- loadfile() loads a lua file into the variable but does not run it, the variable is called again as a function to have it run
    -- load() is loadfile() for strings, the variable is called again as a function to have it run
    -- local functionality that is only ever within local scope and never brought into global scope from the specified file will be treated as invisible and unretrievable
    -- functionality is called using . dot notation

-- eg. the below code is in seperate.lua

local M = {}

local function sayMyName() 
    print("You're Heisenberg")  
end

function M.sayHello()
    print("Why hello there")
    sayMyName()
end

return M

-- eg. the below code is in main.lua

local mod = require("seperate") -- require() brings in all globally scoped functionality from seperate.lua, accessed via the variable mod
mod.sayHello() -- prints "Why hello thereYou're Heisenberg", because sayHello() is in the global scope and it brings in sayMyName() despite that being only within local scope in seperate.lua
mod.sayMyName() -- this FAILS because sayMyName() is in the local scope only

More on