Comprehensive Lua

From Training Material
Jump to navigation Jump to search

Nobleprog.svg


Comprehensive Lua


Introduction to Lua

Note: Many of the examples given in this document are created from examples and code on the website Lua.org or on Lua-users.org or from the book Programming in Lua by Roberto Ierusalimschy (third Edition) Some of these programs have been changed so they are more understandable or use thoughts from all three sources as well as many other sources on the web. See the references in the document for the various sites that have contributed to the examples in the document.

History

  • Development started in 1993
  • Designed to be integrated with C and C++
  • It is simple, extensible, portable, and efficient
  • Used in embedded systems, mobile devices, games, etc.

Goals

  • Simplicity - a small and simple language
  • Automatic memory management (versus C and C++)
  • Extensibility - through Lua code and external C code
  • Efficiency - one of the fastest languages
  • Portability - runs on almost all platforms

References and Tutorials

Wikipedia on Lua
The Lua website
Lua Documentation
Lua Tutorial at TutorialsPoint
Lua.org tutorial
Lua Tutorial at luatut
Lua Tutorial Directory at lua-users.org
Examples of Lua code at Gammon.com
Main Directory at lua-users.org
Quick Reference Card for Lua
Lua-users.org on Lua Quick Cards
Lua Short Reference

Installing an IDE

  • There are several Lua IDEs (Integrated Development Environments)
  • Two of these are
    • Lua Development Tools (based on Eclipse)
    • ZeroBrane (Standalone IDE)

Installing Lua Development Tools

Eclipse Lua Development Tools Standalone Download

  • Quick Instructions
1. Download the Lua Development Tools Standalone Product from the link above
2. Unzip the tool zip file into a folder called Eclipse Lua (so the files will have a folder)
3. Create a directory where everything related to Lua can be placed
     A common practice is to put this on the root of the C disk in Windows
4. Create a folder called workspace in the same folder where you placed Eclipse Lua
     Note that this is the author's preference and not a required methodology
5. On Windows go into Eclipse Lua and right click on the LuaDevelopmentTools.exe
6. Using "Send To" on windows put a shortcut to the executable on the desktop
7. To start the tools double click on the desktop icon on Windows 
8. Choose the workspace you created when asked

Installing ZeroBrane

Zerobrane Website

  • Quick Instructions
1. Go to the Zerobrane website using the link given above
2. Go to the Download page given on the website
3. Choose whether you want to support Zerobrane or
    Choose the link "Take me to the download page this time"
4. Create a folder to hold the all files related to Zerobrane
    A common practice is to put this on the root of the C disk in Windows
    It should be a location that can be found later
5. Choose the download for the type of computer
    If you choose the Windows exe file put it in the folder created in the last step
6. Both the zip file and the exe file for windows give an extracted folder
    The exe file for windows sets some folder preferences 
    so have it in the correct location before executing it
    Move the extracted zip file to the correct location if using the zip
7. Go into the ZeroBraneStudio folder once it is extracted (zip) or created (exe)
8. Using "Send To" on windows put a shortcut to the executable on the desktop
9. To start the tools double click on the desktop icon on Windows

LuaRocks

  • The Lua package manager
  • In a later section LuaRocks will be installed, and used
    • LuaSockets will be used as an example of a LuaRock
      • In a Lua and C program that will do networking

References

Download and Install
LuaRock website

Basic syntax and semantics

Identifiers

  • Any string of letters, digits, and underscores
    • Letters in ranges A-Z and a-z
  • Cannot start with a number
  • Avoid identifiers starting with an underscore followed by an uppercase letter
    • reserved for special uses in Lua
  • Do not use reserved words in the language i.e. for, while, if, else, etc.
  • In general
    • use meaningful names
    • use camel case to allow the reading of meaningful names

Types

  • The main types in the language are:
    • Numbers - represent real numbers in the IEEE 754 standard
Any operation with a result that gives an exact representation
must always give that representation
The IEEE 754 standard makes integers unnecessary
  • Strings - a sequence of characters
Strings are immutable values
When modifying a string a new string is always created
  • Boolean - have values false and true
Conditionals consider false and nil to be false values
Anything else is true
  • nil
nil is a non-value representing the absence of a useful value
  • Functions - a function can be assigned to a variable
  • Table - an associative array
  • The type function gives the type of a variable
      print (type("Hello")) -- gives string
      print (type(true)) -- gives boolean
      local a = 10
      local b = type(a) -- gives number
      print (b)
      print (type(b)) -- gives string

More on Strings

Literal Strings

  • Can use either single (') or double (") quotes
  • The other type of quote can be used within the string

Escape Sequences

 \a - bell
 \b - back space
 \f - form feed
 \n - newline
 \r - carriage return
 \t - horizontal tab
 \v - vertical tab
 \\ - backslash
 \" - double quote
 \' - single quote

Long Strings

-- a long string
longString = [[This is a
very long string
that goes on and on]]
print (longString)

-- another long string
anotherString = [===[This is another
long string that has
some double brackets [[]] in it]===]
print (anotherString)

print("This is a string\nthat goes on also")

Coercions

  • There are automatic coercions between numbers and strings

Tables

  • Tables are associative arrays
    • Can be indexed with number or strings (common way of doing this)
      • or with other values of the language except nil
  • Tables are the main data structures of the language
    • The only primitive data structure
  • They are dynamically allocated
  • A program has references to them
  • They are created by a constructor expression
    a = {} -- creates a table and puts a reference in a
    a["earth"] = "imageEarth.jpg" -- put the filename of an image into a at key "earth"
    print(a["earth"]) -- prints the string "imageEarth.jpg"
    a[10] = "imageMars.jpg"
    print(a[10])
    print("The earth image is " .. a["earth"])

    -- use the alternate syntax for a["earth"]
    print("The earth image is also " .. a.earth)

    local image = "earth"
    print ("This also gives the earth image " .. a[image])

    -- assigning functions to variables (be careful)
    printFunction = print
    printFunction("this is a string")
    a["printIt"] = print
    a.printIt("Printing using an index into a table reference")

Table Constructors

   -- The simplest constructor
   a = {} -- an empty table
   print(a[1])

   -- table indexed by integers
   b = {"Earth", "Mars", "Saturn", "Jupiter"}
   print (b[1])

   -- table with string indices
   c = {x="Earth", y="Mars", z=10}
   print(c.x)
   print(c["x"])
   print(c.z)

   -- nesting tables within tables

   d = {x="Piano", y={"Trumpet", z="Violin"}}
   print(d.x)
   print(d.y[1])
   print(d.y.z)

References

TutorialsPoint on Tables
Lua.org on Tables
Lua.org on Table Constructors

Expressions and Statements

  • Expressions are numeric, relational or string
  • Each has a set of operators that are used in their creation

Arithmetic expressions operators

   addition (+)
   subtraction (-)
   multiplication (*)
   division (/)
   Exponentiation (^)
   Modulo (%)
   Unary negation (-variable)

Relational operators

   Less than (<)
   Greater than (>)
   Less than or equal (<=)
   Greater than or equal (>=)
   Equal (==)
   Not Equal (~=)

Logical Operators

   and
   or
   not

Precedence Rules

  • Arithmetic, relational, and logical operators
    • follow the standard precedence rules

Assignment

  • The equals sign is assignment
    • Assigns a value to a memory location
   variableName = value
  • left-hand side is a reference to a memory location
  • right-hand side evaluates to the value (can be a function reference)

String Concatenation

a = "This is a " .. "string concatenation"

Length of String and Table with integer indices

  • The length operator is the hash (#)
   -- string concatenation and length
   a = "this is a string " .. "concatenation"
   print(a)
   b = #a
   print("The string length is " .. b)

   -- length of a table with integer indices
   b = {"Earth", "Mars", "Saturn", "Jupiter"}
   print (#b)

Comments

  • Similar to most programming languages
    • Lua has single line and block comments

Single Line Comments

  • starts with a double hyphen -- and runs to the end of the line
-- This and the following line are commented out
-- if (a > b) then a = b end

Block Comments

  • Starts with --[[
  • runs until an ending ]]
--[[
if a < b then
   c = 0
   d = a + b
end
]]

Global Variables

  • Variables that are not declared as local in a block
Declarations of variables is not required in Lua
    This means that misspelling etc. can cause errors in the program.
    The misspelling will be taken as a proper identifier with a value of nil.

Various ways have been suggested to correct this problem.

Ways of detecting undeclared variables

Local Variables

  • Created using the local statement
  • scope is limited to the block in which they are declared
    • Block scope
      • Body of a control structure
      • Body of a function
      • Body of the chunk (the file)
      • do end block
  • Local scope overrides global scope

Basic control structures

  • Control structures generally include
    • Looping
    • Conditional branches

The while and repeat loops

  • A while loop and a repeat loop have three required parts
    • A statement that initialize a loop variable
    • A while or repeat condition that tests the loop variable eventually ending the loop
    • A statement within the loop that changes the loop variable so that the loop eventually ends
  • The while loop tests its conditon at its start and may never execute its body
  • The repeat loop always executes once and tests its loop condition at its end
  • The basic structure of the while is the following:

-- set up a loop variable
-- while conditionTestingLoopVariable do
--   body of the loop
--   change the loop variable
-- end

-- an example of the while loop

local i = 1
local a = {"Oak", "Pine", "Cherry", "Fir"}
while a[i] do
   print (a[i])
   i = i + 1
end
  • The basic structure of the repeat loop is the following
    • Note that in this case the loop always runs once
    • Which in this case prints nil to the console
      • a[5] is nil since it is beyond the initialized portion of the table
-- set up a loop variable
-- repeat
--   body of the loop
--   change the loop variable
-- until conditionTestingLoopVariable 

-- an example of the repeat loop

local i = 5
local a = {"Oak", "Pine", "Cherry", "Fir"}
repeat
   print (a[i])
   i = i + 1
until a[i]

The for loop

  • There are two types of for loops in Lua
    • Numeric for loop - usually use integers as the loop variable
    • Generic for loops - use iterators to traverse a data structure
  • The basic structure of the numeric for loop is the following
    • Note that this returns oak and cherry as the results
      • Because of the step of 2 in the loop variable
--for var = exp1, exp2, exp3 do
--   body of the loop that does something
--end
-- exp1 is used to initialize the loop variable
--    which is local to the for loop block
-- exp2 is used to test against the value of the loop variable
-- exp3 tells how much to step the loop variable after each loop

-- An example of a for loop

local a = {"Oak", "Pine", "Cherry", "Fir"}
for i = 1, 4, 2 do
  print(a[i])
end

Generic for loop

  • Loops using an iterator
  • The generic for loop traverses all values return by an iterator for a data structure
    • We will see more data structures to which this could apply in later sections
    • The function pairs returns the index and value of each element of a table in order
local tableDays = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
for k,v in pairs(tableDays) do print(k, v) end

Conditionals if then elseif else end

  • Lua has the standard forms of if and else
    • The syntax is not c or java like however
The form of the if statement is

if conditional then
   -- your code goes here
end

The form of the if else statement is

if conditional then
   -- your if code goes here
else
   -- your else code goes here
end

A simple example of all three statement types is given below

local number1 = 57
local number2 = 43

local number3 = number1 + number2

if number1 > number2 then
  print ("number1 is greater")
end

if number1 < number2 then
  print ("number2 is greater")
else
  print ("number1 is greater")
end

if number3 < number1 then
  print ("number1 is greater than number3")
elseif number3 < number2 then
  print ("number2 is greater than number3")
else
  print ("number3 is greater than both of the other numbers")
end

Using break return and goto

  • The "break" statement is used exclusively to break out of a loop
  • The "return" statement is used to return from a function
    • It is not required if the function returns naturally
    • It does not require a return value
  • The "goto" statement jumps execution to a "label"

The syntax of the "goto" and "label" are given below


goto myLabel

-- some code goes here

::myLabel::

-- some code goes here

Note: Misuse of the goto causes spaghetti code so that care must be taken in using it. Originally it was the common way of looping. Many languages have dropped the "goto" as a construct because of the many problems that it causes with the concept of structured code.

Using do end to delimit blocks

blocks for declarations of local variables can be delimited by do end

do
   local a = 9
   some other code
end

Exercise on Control Structures

Hands on Exercise
Write a program using Eclipse and Lua that will do the following

This is inspired by a project in which inappropriate words had to be
removed from the text of user blog postings.  We will just use Tables
with words for both the text and the check words.  This makes it simpler.

Note: in this exercise use words such as "and", or "the" or "tree" as the
check words so that others can view your work.  Usually it is bad form to
but actual swear words into programs.  The case mentioned above being the
exception.

1.  Create two tables that are initialize with words use numeric indices
    note that this can be done using tableText = {"This", "is", "my", "table"}
2.  Using a while loop compare the text word table against the check word
    table
3.  If check words appear in the text words then remove them from the text
    words using table.remove
4.  Keep the check words found in the text words in a new table
5.  Use a generic for loop to print out the check words that matched
6.  Use a for loop to concatenate the text works back into a string with
    spaces between the text words then print out the text

Note that concatenation of words is a poor way of building text since
concatenation causes new strings to be built on each operation.

The alternative is to use table.concat(tableIdentifier)

Functions

  • The main mechanism for abstraction
    • Can carry out a task
    • Can return values
  • A Function has the form
function name (parameter list)
   code goes here
   ...
   return optional
end
  • parameters work as local variables
    • With function block scope

Variadic Functions

  • A variadic function receives a variable number of arguments

An example of a Variadic Function

local a = 7 b = 8 c = 9 d = 5

function add(...)
  local sum = 0;
  for i,v in ipairs{...} do
    sum = sum + v
  end
  return sum
end

print (add(a, b))
print (add(a, b, c, d))

Returning multiple results

  • Functions can return multiple results
function multipleResults (list of parameters)
   code goes here
   ...
   return variable1 variable2 variable3
end
  • The returned results are adjusted to match the call
   -- set up some local variables
   local i = 5
   local j = 4
   local k = 8
   -- the example function
   function example1(a, b, c)
      local var1 = a + 5
      local var2 = b + 1
      local var3 = c + 10
      return var1, var2, var3
   end
   -- call the function and return one value
   local result1 = example1(i, j, k)
   print ("result1 is " .. result1)
   -- call the function and return three values
   local result2, result3, result4 = example1(i,j,k)
   print ("result2 is " .. result2)
   print ("result3 is " .. result3)
   print ("result4 is " .. result4)
   result1, result2, result3, result4 = 21,22, example1(i, j, k)
   print ("* result1 is " .. result1)
   print ("* result2 is " .. result2)
   print ("* result3 is " .. result3)
   print ("* result4 is " .. result4)
   result1, result2, result3, result4 = example1(i, j, k), 21,22
   print ("- result1 is " .. result1)
   print ("- result2 is " .. result2)
   print ("- result3 is " .. result3)
   print (result4) -- returns a nil value

Exercise on Functions

Hands on Exercise
   Using Eclipse do the following:
      
      1. Get an operation using io.read() operations are add, sub, mul, div
         - Save the operation in a table
      2. Get two numbers from the user using io.read()
         - Save the operands in a table
      3. Create a function that will perform the operation
        - The first number will be the numerator for division
        - If division will divide by zero then return a nil
        - otherwise return the result
      4. Save the result 
         - If there was an error then give an error message (in case of div by 0)
      5. print out the result and ask the user for another operation and one number
         - The result of the last operation will be the other number (the first)
      6. Continue this looping until the user enters bye as the operation
      7. Output all of the operations and operands in an understandable format to the console
         - Using print()
         - include the total
      6. do some calls to exercise your function
      7. Single step through the code using the Eclipse Debugger

Named Arguments

  • Arguments are passed in Lua by position
    • First argument goes to first parameter etc.
  • Arguments can be given names by instead passing a table
    • The table will have name value pairs
    • Thus the arguments being in a table can have associated names

An example of passing both types of arguments is given below

local arguments = {planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"}, 
                   stars = {"Sun", "Rigel", "Altair"}}
local celestialType = "planets"

function printCelestialType (argTable, printType)

  local theType = nil
  if printType == "planets" then
    theType = "planets"
  else
    theType = "stars"
  end

  tableToPrint = argTable[theType]
  for index, value in pairs(tableToPrint) do
    print (value)
  end
end

printCelestialType(arguments, celestialType)

Closures

  • A function enclosed within another function
    • has access to all of the local variables of the enclosing function

Exercise on Closures

Trace the following code in a debugger

1. Determine how name and ages are used in each function
2. Discuss and explain to the group
local names = {'Carol', 'Jess', 'John', 'Daniel' }
local ages = {Carol = 20, Jess = 14, John = 41, Daniel = 63}

table.sort(names, function (name1, name2)
  return ages[name1] > ages[name2]  -- ages is an upvalue here, can be seen
end)

for i = 1, #names do
  print(names[i])
end

names = {'Barbara', 'Ben', 'Harry', 'Jen' }
ages = {Barbara = 20, Ben = 14, Harry = 41, Jen = 63}

function sortByAge (names, ages) -- references names and ages are local to sort
  table.sort(names,function (name1, name2) -- names and ages can be seen within sort
    return ages[name1] > ages[name2] -- ages can be seen within anonymous function
  end)
end
sortByAge(names, ages)
print("\n\n\n")
for i = 1, #names do
  print(names[i])
end


names = {'Barbara', 'Ben', 'Harry', 'Jen' }
ages = {Barbara = 20, Ben = 14, Harry = 41, Jen = 63}

function sortByAge () -- call has two parameters neither of which are used
  function ageCompare(name1, name2) -- ages and names are upvalues here
    return ages[name1] > ages[name2] -- ages can be seen within ageCompare
  end
  table.sort(names,ageCompare)  -- references names and ages can be seen within sortByAge
end
sortByAge(names, ages)  -- passed in names and ages in the call but they are not used
print("\n\n\n")
for i = 1, #names do
  print(names[i])
end
  • In the examples the ages variable within the function is called a non-local variable
    • It is not global and it is not local
    • This is sometimes referred to as an upvalue
    • It can be seen because of the scoping rules of Lua

Closure definition - A closure is a function plus all that it needs to access non-local variables correctly

Trace through the following code using the debugger

1. Determine how the closures work
2. Explain and discuss
function counter ()
  local i = 0;
  return function ()
    i = i + 1
    return i
  end
end

theCounter = counter()
print(theCounter()) -- the function reference continues to refer to the same function
print(theCounter()) -- and its local variables (including its parameters)

-- iterators and closure

function iterator(theList)
  local i = 0
  return (function () i = i + 1; return theList[i] end)
end

local numbers = {"Earth","Mars","Saturn","Venus"}
iter = iterator(numbers) -- get a reference to the function
while true do
  local theItem = iter() -- use the function
  if theItem == nil then break end
  print (theItem)
end

Tail Calls

  • A tail call happens when a function makes a function call in its return
    • No other work must be required after the function returns
      • return theFunction(x, y, z) would be a proper tail call
      • return theFunction(x, y, z) + 1 would not be a proper tail call
  • This can be used for recursion when it function calls itself in its return
  • It can be used to return the result of another function

In computer science, currying is the technique of transforming a function taking multiple arguments into a function that takes a single argument (the first of the arguments to the original function) and returns a new function that takes the remainder of the arguments and returns the result.

Reference Curried Lua
  • By the use of currying a function with several parameters can be modified
    • Into several functions with single parameters

The following is an example of currying to reduce the number of parameters per function

function test1 (x, y, z)  -- add three number with three parameters
  return x + y + z
end

print (test1(5, 4, 3))

function f1 (x)  -- add three numbers with one parameter per call, curried
  return function (y)
    return function (z)
      return x + y + z
    end
  end
end

f2 = f1(5)  -- calling the curried functions
f3 = f2(4)
print (f3(3))

References

Curried Lua

Exercise on Functions (Polynomial Evaluation)

Note that this exercise is taken from "Programming in Lua" by Roberto Ierusalimschy

Hands on Exercise

A polynomial can be written as y = anxn + an-1xn-1 + ... + a0x0

Write a function that performs polynomial evaluation as a function with a curried tail call.
A. This function will take one parameter that will be a table of the "a" coefficients
B. A curried tail call is a call that returns a function
C. Generally that function is written as an anonymous function as part of the return statement
D. The returned function will take one parameter that will be the value of the variable "x" 
E. The polynomial evaluation will be in the anonymous function

Data Structures Using Tables

Tables in Lua

  • Tables are associative arrays
    • Basically Hash Tables
    • Name value pairs
      • What is a hash
      • Why is a hash fast
  • Tables are the only actual data structure in Lua
  • All other structures are made using them

Arrays

  • Arrays are sequential tables
    • without nils at intermediate locations
  • Arrays are made up indexing tables by integers
  • Customarily arrays in Lua start at 1
    • You can start them at any integer index
  • The length hash (#) works with arrays
    array1 = {};
    for i=1, 10 do
       array1[i] = "value" .. i
    end
    
    for i=1, 10 do
       print(array1[i])
    end
    print("\n\n\n")
    
       -- using a constructor to initialize and array
    array2 = {"value1", "value2", "value3", "value4",}
    for i=1, #array2 do
       print(array2[i])
    end

Multi-Dimensional Arrays

  • Multi-dimensional arrays are created with tables
  • Basically they are arrays of arrays
   array1 = {}

   for i=1, 4 do
      array1[i] = {}
      for j=1, 4 do
         array1[i][j] = "value ".. i .. " " .. j
      end
   end
   
   for i=1, 4 do
      for j=1, 4 do
         print(array1[i][j])
      end
   end

Linked Lists

  • Linked Lists are created with Tables
  • Each element is an array with fields next and value
   function addToHead (linkedList, itemValue)
      if linkedList == nil then
         local temp = {next="endOfListFlag", value = itemValue}
         return temp
      else
         local temp = linkedList
         linkedList = {next=temp, value = itemValue}
         return linkedList
      end
   end
   
   
   local linkedList = nil
   linkedList = addToHead(linkedList, "first element on list")
   linkedList = addToHead(linkedList, "Second element on list")
   linkedList = addToHead(linkedList, "Third element on list")
   linkedList = addToHead(linkedList, "Fourth element on list")
   
   local root = linkedList
   local done = false;
   while not done do
     print(linkedList.value)
     if(linkedList.next == "endOfListFlag") then
        done = true
     end
     linkedList = linkedList.next
    end

Stacks

  • A stack is a LIFO structure (last in first out)
  • Example code is below
   stack = nil
   stack = {next=stack, value = "first element on list"}
   stack = {next=stack, value = "second element on list"}
   stack = {next=stack, value = "third element on list"}
   stack = {next=stack, value = "fourth element on list"}
   
   root = stack
   while stack do
     print(stack.value)
     stack = stack.next
    end

Exercise on Stacks

Hands on Exercise
   Using Eclipse do the following:
      1. Write a function that will push items on to a stack
      2. Write a function that will pop items off of a stack
      3. Exercise your function in Eclipse to prove that it works
      4. Include several calls to push items
      5. Include calls to pop items and then print out their values
      6. Single step through your code using the Eclipse debugger

Queues

  • Queues are similar to stacks
  • They are FIFO structures (First In First Out)
  • The coding is similar to that for stacks or linked lists

Sets

Examples of sets are given in later sections

String Buffer

  • Reading in long strings one line at a time from a file
    • Should not use string concatenation operator (..)
      • A new string is created during each operation
  • Instead read the lines into a table
    • Then use table.concat(tableName)

Graphs

  • Graphs are basically the same as linked list with the exception
    • They can have links to multiple other nodes

Exercise on Graphs (Work in teams)

1. Create a data structure that can represent a graph
   -- The graph nodes should each contain an identifier and some string content
   -- The string content could, for example, be city and state names with airline routes as links
2. Create a set of functions that can add an node to the graph
3. Create a function that can print out a single node and its links

Tables as Objects

Objects vs Values

  • Object types in Lua have references rather than values
    • Object types in Lua: functions, tables (also thread like structures and userdata)

The differences are shown in the following

  • The Table data structure requires both memory and code to access its elements
-- constructor expression
planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"} 

a = planets  -- passes a reference to the table data structure

print(a[1]) -- uses the syntax for accessing the data structure

b = 5  -- initialize a memory location

c = b  -- move the data to a new memory location

print(c) -- print the content of a memory location

Errors and Error handling

  • Programs are going to have errors
  • Therefore we must handle errors in appropriate ways
    • Stop the program when there is a major error
    • Handle errors when possible

Stop the program on major errors

  • Errors can be thrown using the error function
  • This immediately stops the program with an error
  • A second parameter can be put on the error function
    • It gives the level for the error
    • Allows the error to be passed to the calling code
Calling code must then handle the error
Present code is level 1
The calling code is level 2
error('invalid input not a number', 2)


    local a = "this is a string"
    if type(a) ~= 'number' then
        error('invalid input not a number')
    end
  • The assert function can test a condition and throw an error
When the condition returns false or nil
  • The message of the assert is shown as the error message
  • This immediately stops the program with an error
    local a = "this is a string"
    assert(type(a) == number, "this is not a number")

Catch and handle errors when appropriate

  • To catch an error the pcall function is used
    • pcall calls its first argument in protected mode
    • If there are no errors pcall returns true
    • If there are errors pcall returns false
  • To use pcall encapsulate the code
  • Encapsulate the code in an anonymous function
    • Becomes the parameter for pcall
  • pcall expects to receive two return results
    • A Boolean that tells whether the function ran successfully
    • A msg giving the error if it did not run sucessfully
local ok, message = pcall(function ()
  local a = 50
  local theType = type(a)
  assert(theType == "number", "this is not a number") --fails the program immediately

  local a1 = "this is a string"
  if type(a1) ~= 'number' then
    error('invalid input not a number') --fails the program immediately
  end
  assert(type(a) == "number", "the input is not a number")
end)

if ok then
  print("Sucessful continue on with code here")
else
  print ("Error " .. message)
end

Give a stack trace with the error message

  • To get a stack trace use the xpcall function
    • Allows a second parameter that is an error handler
    • In the error handler call debug.traceback(err)
variable1 = 50
variable2 = "this is a string"
function someFunction()
  local a = variable1
  local theType = type(a)
  assert(theType == "number", "this is not a number") --fails the program immediately

  local a1 = variable2
  if type(a1) ~= 'number' then
    error('invalid input not a number') --fails the program immediately
  end
  assert(type(a) == "number", "the input is not a number")
end

function errorHandler(err)
  return debug.traceback(err)
end

local ok, message = xpcall(someFunction, errorHandler)

if ok then
  print("Sucessful continue on with code here")
else
  print ("Error " .. message)
end

Iterators

  • An iterator iterates over elements of a collection
    • Can be represented as a function
Each call to the function returns the next element in the collection
  • An iterator can keep state
    • What element is the next element to return
or what was the last element returned

An Example of a Table List Iterator

-- iterators and closure

function iterator(theList)
  local i = 0
  return (function () i = i + 1; return theList[i] end)
end

local planets = {"Earth","Mars","Saturn","Venus"}
iter = iterator(planets) -- get a reference to the function
while true do
  local theItem = iter() -- use the function
  if theItem == nil then break end
  print (theItem)
end

The generic for loop

An example of a for loop using an iterator

local planets = {"Earth","Mars","Saturn","Venus"}
for item in iterator(planets) do
  print (item)
end
  • The generic for loop allows (see code below):
    • The var-list to be a list of one or more variables names
      • Separated by commas
    • The exp-list to be a list of one or more expressions
      • Separated by commas (usually this has one element)

Syntax of the generic for loop

for <var-list> in <exp-list>
  <body>
end

Stateless vs stateful iterators

  • Stateful iterators have been given as examples in prior code segments
  • Stateless iterators must:
    • Pass the state out to the calling code in the return statement
      • Usually the return statement passes multiple values
    • Pass the state in from the calling method in the function arguments

Examples of this are ipairs and pairs

  • ipairs iterates over the portion of a table without holes
  • pairs iterates over the entire table (in any order)
local orchestra = {'violin', 'trumpet', 'clarinet', 'tuba', 'guitar', 'flute'}

for i, instrument in ipairs(orchestra) do
  print ("Instrument" .. i .. " " .. instrument)
end

orchestra = {[1] = 'violin', [2] = 'trumpet', [6] = 'clarinet', [18] = 'tuba', [20] = 'guitar', [3] = 'flute'}
for i, instrument in pairs(orchestra) do
  print ("Instrument" .. i .. " " .. instrument)
end

Results are

  • Note that the second set are not in order
Instrument1 violin
Instrument2 trumpet
Instrument3 clarinet
Instrument4 tuba
Instrument5 guitar
Instrument6 flute

Instrument1 violin
Instrument20 guitar
Instrument2 trumpet
Instrument18 tuba
Instrument3 flute
Instrument6 clarinet

Behind the covers something like the following is doing the iteration

    function iter (a, i)
      i = i + 1
      local v = a[i]
      if v then
        return i, v
      end
    end
    
    function ipairs (a)
      return iter, a, 0
    end

Exercise on Iterators

Hands on Exercise
Using Eclipse do the following:

1. Using the code created before for pushing and popping a stack initialize a stack
2. Create an iterator for a stack using closure and a function that passes in a stack
3. Write a for loop that iterates through the stack and prints out its values

Coroutines

  • A coroutine is similar to a thread in that:
    • It has its own stack
    • It is a line of execution
    • It has its own local variables
    • It has its own instruction pointer
  • A coroutine shares global variables
  • A set of coroutines in a program are collaborative:
    • Only one is running at a time
    • It suspends execution only when it explicitly requests to be suspended

Producer Consumer Example

This discussion follows the discussion on the Lua Website at url Pipes and Filters

It gives a good example of cooperating coroutines
  • Trace through the function by hand first
  • Then put it into Eclipse and try it with the debugger
    • Put breakpoints on coroutine.yield and coroutine.receive
--- producer consumer using coroutine resume/yield to synchonize
--- and pass data between the producer and consumer
function receive()
  local status, value = coroutine.resume(producer) -- starts when other function yields
  return value
end

function send(x)
  coroutine.yield(x) -- yields and send value to resume
end

producer = coroutine.create( -- create the producer that runs the anonymous function code
  function ()
    while true do
      local x = io.read('*L')
      if x == nil then os.exit(true, true) end  -- kills the program when true
      send(x)  -- calls the send function which does a yield sending data
    end
  end)

function consumer()
  while true do
    local x = receive()
    io.write("input was -- ", x, "\n")
  end
end

io.input("C:\\lua Programming\\producerFile.txt")
consumer()

Metatables and metamethods

  • Metatables are similar to operator overloading in C++
  • Metatables can be assigned to Tables
    • Using setmetatable(theTable, theMetatable)
    • There is also a getmetatable(theTable) which gives the metatable reference
  • Metatables contain function definitions which are called metamethods
    • The metamethods can be used to manipulate tables

The set example is given below

This is from Programming in Lua by Roberto Ierusalimschy
local mt = {}
Set = {}

function Set.new (l)   -- 2nd version
  local set = {}
  setmetatable(set, mt)
  for _, v in ipairs(l) do set[v] = true end
  return set
end

function Set.union (a,b)
  local res = Set.new{}
  for k in pairs(a) do res[k] = true end
  for k in pairs(b) do res[k] = true end
  return res
end

function Set.intersection (a,b)
  local res = Set.new{}
  for k in pairs(a) do
    res[k] = b[k]
  end
  return res
end
function Set.tostring (set)
  local l = {}
  for e in pairs(set) do
    l[#l + 1] = e
  end
  return "{" .. table.concat(l, ", ") .. "}"
end

function Set.print (s)
  print(Set.tostring(s))
end

local s1 = Set.new{10, 20, 30, 50}
local s2 = Set.new{30, 1}

print(getmetatable(s1))
print(getmetatable(s2))

mt.__add = Set.union
mt.__mul = Set.intersection
--mt.__tostring = Set.tostring

s3 = s1 + s2

Set.print(s3)

Set.print((s1 + s2)*s1)

print((s1 + s2)*s1)

Arithmetic Metamethods

  • Metamethods can be defined for:
    • Addition (+) __add
    • Subtraction (-) __sub
    • Multipication (*) __mul
    • Division (/) __div
    • Modulo (%) __mod
    • Exponentiation (^) __exp
    • Negation (unary -) _unm
    • Concatenation (..) __concat
  • It might be that some of these operators make no sense in terms of the types involved

Relational Metamethods

  • Metamethods can be defined for:
    • Equal (==) __eq
    • Less Than (<) __lt
    • Less Than or equal (<=) __le
  • For the other operators the operands are reversed
    • For example greater than (a>b) is the same as (b<a)
  • For a ~= b the expression not(a == b) is used

Library and Table Access Metamethods

The metatable __toString method

  • It is common for libraries to define metatable and metamethods
    • The print command looks for the __tostring metamethod
      • The effect of putting the tostring method in the metatable can be seen:
uncomment the mt.__tostring = Set.tostring line in the example for sets

The __index and __newindex metamethods

  • When an access to an index in a table returns a nil
  • The interpreter looks for an __index metamethod
If found the metamethod returns the result rather than nil
__index can be set to a function, or a table
If set to a table a lookup is done in the table for the key passed in
If a function the function is called with the table and key as parameters
To access a table without invoking __index Use rawget(t,i)
  • __newindex is used for table updates
    • if the index absent then the __newindex function is called if it exists
    • rawset(t,i) is used to bypass the check
    • If a table is assigned to __newindex then the assignment is made in this table

Modules and packages

  • Modules are intended to:
    • Allow groups to share code
    • Prevent collisions between the identifiers in different Modules
  • Lua has Policies for modules and packages
    • The attempts is to define these using standard Lua structures
      • Tables
      • Functions
      • Metatables
      • Environments

Using Modules

  • A module is some code that can be loaded through the "require" statement
    • Can be a combination of Lua and C code
    • The "require" statement returns a table
    • The table acts as a namespace
    • The table contains constants, functions, etc.
    • The standard libraries are modules

An example of using the math library

local math = require "math"
local table1 = {}
for i = 1, 10 do
  table1[i] = math.random(1, 200)
  print(table1[i])
end
  • require cannot pass arguments
    • This restriction is because the structures used are a normal part of the language
    • You can easily create functions in the module that will allow the passing of arguments

How are modules Loaded

  • On the first require statement for a module
    • Lua searches for the Lua file with the module name
    • If it finds a Lua file it calls "loadfile" which gives a loader for the file
    • If it cannot find a Lua file it looks for a C library with the name
    • If a C file is found it calls package.loadlib which gives a loader for the file
    • Require now calls the loader to load the module
    • Require returns the return value from the loader and puts it into the package.loaded table
    • Upon subsequent calls to require the package.loaded file has the reference to the module

Path Searching

  • Lua uses a set of templates to search for modules rather than a set of paths
    • Each template is a path with optional question marks (?)
    • The templates in a path are separated by semicolons
    • The question marks are replaced by the module name given to require
  • The path for finding Lua files comes from variable package.path
    • Package.path is loaded when Lua starts up from LUA_PATH
      • LUA_PATH is in the environment variables of the operating system
      • If this cannot be found Lua uses a default path
  • The path for finding C files comes from variable package.cpath
    • package.cpath comes from environment variable LUA_CPATH

Module Search Path in Package Table

Creating Modules

  • Modules can be created as tables containing functions etc.

A example that creates a new module

newModule = {}

local masterTable = {}
local i = 1
function newModule.addString(inputVariable)
  if type(inputVariable) ~= "string" then
    print("Error - input is not a string")
  else
    masterTable[i] = inputVariable
    i = i + 1
  end
end

function newModule.printStrings()
  for j = 1, i-1 do
    print(masterTable[j])
  end
end
return newModule

An example of loading and run the new module

local testModule = require "moduleSample"

testModule.addString("This is the first string")
testModule.printStrings()

Organizing modules into packages

  • Lua allows modules to be organized into packages
    • This is similar to the packages of Java
    • Folders are created with Lua files in them
    • The dot notation specifies the package hierarchy
    • It also specifies the folder hierarchy

An example of the code in a module

newModule = {}

local masterTable = {}
local i = 1
function newModule.addString(inputVariable)
  if type(inputVariable) ~= "string" then
    print("Error - input is not a string")
  else
    masterTable[i] = inputVariable
    i = i + 1
  end
end

function newModule.printStrings()
  for j = 1, i-1 do
    print(masterTable[j])
  end
end
return newModule

An example of using Lua modules and packages

local testModule = require "moduleSample"
local anotherModule = require "modules.sample"

testModule.addString("This is the first string")
testModule.printStrings()


anotherModule.addString("This is the first string from the modules package")
anotherModule.printStrings()

Below is an image of Eclipse showing packages and modules Lua Modules and Packages in Eclipse

Metatables through examples

A number of examples to walk through are given in this section

  • Giving default values for tables
  • Checking every read and write to a table with a proxy
  • Making a table read only
  • Memorizing results for reuse through memoization


Default Values for Tables

Problem Definition
Assume that it is necessary to have default values on a set of tables

If the table does not have a value for a key then
A default value is to be given. Note that in this case
only one default per table is allowed


Problem Solution Discussion

1. Each read of a value for a key from the table will 
   return a value or if the key does not exist in the table a nil.
2. A call to return an absent key can be intercepted by defining 
   the __index metamethod.
3. The __index metamethod can then return a default value
4. The question becomes how to make the metatable efficient

Solution 1

--[[This is a function which will set a default for a table.
    Note that a new metatable is created for each table.  The function
    creates only one default per table.  Each use of this function creates 
    a closure this can be expensive in terms of memory used (metatable and default) 
    if there are many tables]]
function setDefault(table, default)
local metatable = {__index = function () return default end} -- a new closure, when __index is called, default is known
setmetatable(table, metatable)
end

theTable = {x=10, y=20}
print(theTable.x, theTable.z)

setDefault(theTable, -1)
print (theTable.x, theTable.z)

setDefault(theTable, 0)
print (theTable.x, theTable.z)

Solution 2

--[[Here we set default values for indices that are used that have no table entries
    by only using one metatable, saves memory.  The function saves the default
    data in the table itself.  It uses an oddly named variable, three underscores (___)
    as the variable name]]
local metatable = {__index = function(t) return t.___ end}
function setDefault(table, default)
table.___ = default
setmetatable(table, metatable)
end

theTable = {x=10, y=20}
print(theTable.x, theTable.z)

setDefault(theTable, -1)
print (theTable.x, theTable.z)

Solution 3

--[[set default values for indices that are used that have no table entries
    by only using one metatable, saves memory.  The function here saves the default
    data in the table itself.  In this case it uses the name "key" as the variable name
    
    Note: to create key it is set up as a table and created using a local variable.  Its
    value is then initialize with the default (which can be of any type) within the
    setDefault function.  This is somewhat obscure and subtle, we end up with an
    index and value pair where the index is an empty table (called key) and the value 
    is the default]]
local key = {}
local metatable = {__index = function(t) return t[key] end}
function setDefault(table, default)
table[key] = default
setmetatable(table, metatable)
end

theTable = {x=10, y=20}
print(theTable.x, theTable.z)

setDefault(theTable, -1)
print (theTable.x, theTable.z)

Exercise on Default Values

1. Run the code for metatablesDefaultRequire from the code samples
2. Determine how the code works
3. Design and create code that will allow multiple default values to be created per table.
   Presently only one default per table is allowed.

Proxies

Problem Definition Assume that all accesses to a table are to be:

    • Tracked
    • Limited
    • Modified
    • Some of the above

Problem Solution Discussion

1. Some type of Proxy standing in front of the table should be present<br>
2. The proxy needs to call some function for every read or write to the table
3. One suggestion is to use the __index and __newindex metamethods.
   With an empty table that acts as the proxy

This suggestion is used for the following code
The solutions are taken with some modifications from 
    Programming in Lua by Roberto Ierusalimschy

Solution 1

--[[In this code the task is to monitor all accesses to a table.  
    To do this we create a proxy for the table.  This is an empty
    table that has both __index and __newIndex metamethods.  Each
    call is made to the proxy table which checks the calls and then
    passes them onto the real table.]]
    
theTable = {}

local _t = theTable
theTable = {}

local mt = {
__index = function( table, key)
  print("*access to element " .. tostring(key))
  return _t[key]
  end,
  
__newindex = function(table, key, value)
  print("*update of element " .. tostring(key) .. " to " .. tostring(value))
  _t[key] = value
  end
}

setmetatable(theTable, mt)

theTable[2] = "hello"
print(theTable[2])

Exercise on Proxies

1. Put the code for metatableProxyModule.lua and metatableProxyRequire.lua
   into Eclipse
2. Run the metatableProxyRequire.lua file
3. Determine how it works and how it differs from the code in the last section
4. Discuss with instructor and others in the class

Read Only Tables

Problem Definition

Construct a table that is read only. Users will not be allow to add new indices

Problem Solution Discussion

A read only table can be constructed using a proxy<br>

   1. The proxy table will be empty
   2. The __index metamethod will be assigned the original table.
      This will cause accesses (reads) to be looked up in the table
      that is in __index (this is the definition of __index it is either
      a function or a table)
   3. The __newindex metamethod will be a function that returns an error
      This will cause a updates (writes) to be rejected.  Since the proxy
      table is empty __newindex will be called for each write to the table
      and an error will be returned.

Solution

The code of the module for the read only proxy is the following

--[[Take a table and make a proxy for that table that will be empty and
    therefore call the __index and __newindex metamethods for every access.
    The _newindex metamethod will return an error so that no new indices can
    be made in the table.  The _index method will look up the value in the
    table which has been assign to the __index metamethod]]
function readOnly (t)
  local proxy = {}
  local mt = {
    __index = t,
    __newindex = function(table, key, value)
      print("attempt to update a read-only table, operation " .. key .. " = " .. value .. " not performed")
    end
  }
  setmetatable(proxy, mt)
  return proxy
end

The code that exercises the module is the following

--[[This is the code to exercise the metatableReadOnly.metatableProxyModule code]]


require "metatableReadOnly.metatableProxyModule"
local days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
days = readOnly(days)
print(days[1])
days[2] = "None"

local planets = {planet1="Mercury", planet2="Venus", planet3="Earth", 
                            planet4="Mars", planet5="Jupiter", planet6="Saturn", planet7="Uranus", planet8="Neptune"}
planets = readOnly(planets)

print(planets.planet8)
planets.planet8 = "Xergon"

Memoization

Memoization refers to memorizing values already used
An example of this is given below

  • Note that the __mode will cause the table to be weak
  • This is discussed in the section on Garbage Collection and Weak Tables
--[[This is a function and some code that shows the concept of memorization]]
local metas = {}
setmetatable(metas, {__mode = "kv"})
function setDefault(table, default)
  local mt = metas[default]
  if mt == nil then
    mt = {__index = function() return default end}
    metas[default] = mt  --this is the line that memorizes
  end
  setmetatable(table, mt)
end

planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"}

local default = "Not in our solar system"
setDefault(planets, default)

print(planets[3])
print(planets[10])

star = {"Sun"}
setDefault(star, default)
print(star[1])
print(star[2])

print(getmetatable(planets))
print(getmetatable(star))
print(metas[default])

Environments

  • Global variables are kept in the Global Environment table
    • The Global Environment is stored in variable _G

Examples of using _G

value = _G[varname] -- get the value of a global variable
_G[varname] = value -- set the value of a global variable
varname = value  -- much easier way to set the value

Declaring Global Variables

  • They do not need to be declared, declared when used
  • Can cause errors (a typo will cause a global variable)

Setting a metatable on _G to give errors when globals are nil

setmetatable(_G, {
  __newindex = function (t, n, v)
    local w = debug.getinfo(2, "S").what
    if w ~= "main" and w ~= "C" then
      error ("attempt to write to undeclared variable " .. n, 2)
    end
    rawset(t, n, v)
  end,
  __index = function(_, n)
    error("attempt to read undeclared variable " .. n, 2)
  end

})

-- a = 9
print(a)
  • The problem with the above is that a new variable that is initialize to nil cannot be set
    • Without using rawset(table, key, value)
  • This metatable also effects the entire program including standard libraries

Relationship between global variables and environments

Free Names

  • Free names (Free Variables) are identifiers that are not within the context of
    • A local variable name
    • A for loop control variable
    • A function parameter
  • In other words they are what we have been calling Global Variables

_ENV and _G

  • _ENV is a local variable associated with a chunk (a lua file)
  • _ENV is initialize with a reference to the global table _G as the chunk is loaded
  • Each free variable in a chuck is referenced as _ENV.variableName
    • Since _ENV is a reference to _G any free variable goes into _ENV and _G
  • Therefore you can use _ENV within a file to limit the functions and globals that are seen
  • Function names are just global identifiers for function references

View of _ENV after it has been limited

_ENV variable in a debugger

  • Note that if _ENV is set to nil then references to existing free variables are lost

The _ENV table and the _G table

  • _ENV is associated with a chunk (a file)
  • _G is associated with the global environment of the program
    • _G is built as the program is loaded
  • _ENV in Lua 5.2 can be overridden by a local _ENV
a = 1
b = 2

local _ENV = {print = print, _G = _G}

c = 5
d = 7

print(_G.a)
print(_ENV.c)

Object-oriented Programming

  • Tables can be used to create classes in Lua
    • They can have name value pairs which act like variables in a class
    • They can reference functions by named indices which act like methods
    • They can reference themselves using the identifier "self" (a new concept)

The concept of self

Self refers to the present object (Tables are objects in Lua).  When the colon (:)
operator is used to call a function in the object/table self is passed into the
function (now referred to as a method) as a hidden variable.  Internally
in the function self can now be used to refer to an instance of a table.

This allow the functions to operate on different instances of tables giving
the ability to have object oriented programming (OOP).  Internally the functions
will use self to refer to their variables and functions

function Account:deposit(v)
   self.balance = self.balance + v --refers to a balance value within this table instance
end


Classes

  • Classes can be created using tables as described above
They will have name value pairs, name function pairs, and use self

An example of a class for a bank account

Account = {balance=0}

function Account:new (theObject)
  theObject = theObject or {}
  setmetatable(theObject, self)
  self.__index = self
  return theObject
end

function Account:deposit (theDeposit)
  self.balance = self.balance + theDeposit
end

function Account:withdraw(withdrawal)
  if withdrawal > self.balance then
    error ("insufficient funds")
  end
  self.balance = self.balance - withdrawal
end

function Account:print()
  print ("Account Balance is " .. self.balance)
end


local newAccount = Account:new()
newAccount:deposit(100.00)
newAccount:print()


local myAccount = {}
Account:new(myAccount)
myAccount:deposit(50.00)
myAccount:withdraw(25.00)
myAccount:print()  -- using the : passes in self as a hidden parameter
--myAccount.print() -- this will not work because self is not present

Inheritance

  • A new table can be built that inherits from the Account table
  • The new table will have the same __index function in its metatable
  • So it will inherits the functions and variables of Account

An example of inheritance Put this code and the prior code into files and try them They can also be found in the sample files for the course

local account = require "objectOriented.classAccount"

SpecialAccount = Account:new()

function SpecialAccount:withdraw(v)
  if v - self.balance >= self:getLimit() then
    error "insufficient funds"
  end
  self.balance = self.balance - v
end

function SpecialAccount:getLimit()
  return self.limit or 0
end


newAccount = SpecialAccount:new{limit=1000.00}

newAccount:deposit(100.00)

newAccount:withdraw(200.00)

newAccount:print()

local limit = newAccount:getLimit()

print("Account limit is " .. limit)

Exercise on Object Oriented Programming

Take the code in folder objectOriented from the sample code and do the following

1. Use the code for classAccount.lua as an account
2. Modify the code for classSpecialAccount so that classSpecialAccount.lua is a module
3. Create a new file called createSpecialAccount.lua that will create a specialAccount
   from the specialAccount module and package
4. Exercise the specialAccount to prove that it works
5. Modify the specialAccount so that it has a printLimit method
6. Modify the specialAccount so that it has a changeLimit method
   that can change the limit on the special account

A walkthrough of the Lua Standard Libraries

Base Library

  • The base library is used without a library name modifier
  • It contains a set of commonly used functions


I/O Library

References

Lua Input and Output Facilities

The Simple I/O Model

  • Commonly uses stdin (input) and stdout (output)
    • This can be changed using io.input(filename) and io.output(filename)

Writing to stdout or a file

   io.write("this is an output string to the console\n")

   io.output("C:\\lua Programming\\myOutputFile.txt")
   io.write("this is an output string to a file\n")
   io.write("this is an second output string to a file")
   io.flush()
   io.close()

   stdoutFile = io.stdout --reset to stdout
   io.output(stdoutFile)

   io.write("This is another output string to the console")

Reading from stdin or a file

  • The io.read command takes one argument
    • They control what to read
   "*a" - read the whole file
   "*l" - read the next line does not return the newline
   "*L" - read the next line returns the newline also
   "*n" - read a number
   num - read a string with up to num characters
  • The following is an example of reading
    • First from stdin with a prompt
    • Then from a file after switching to a file
    • Then back to stdin in with a prompt after switching back to stdin
   print('Enter your First Name:')
   local firstName = io.read('*l')

   io.input("C:\\lua Programming\\myInputFile.txt")
   local theString = io.read('*L')
   io.write(theString)
   theString = io.read('*l')
   io.write(theString)

   stdinFile = io.stdin --reset to stdout
   io.input(stdinFile)

   print('\n\nEnter your Last Name:')
   local lastName = io.read('*L')
   io.write("Thanks for the input " .. firstName .. " " .. lastName)

The Complete I/O Model

  • based on file handles
    • An open file with a current position
      • Open a file using the io.open function
      • Plus a mode string 'r' or 'w' or 'a'
read, write, append
  • Returns a new handle for the file
  • In case of error returns nil

Input

local file = assert(io.open(filename, "r"))
local text = file:read("*a")  -- read the entire file
file:close()

Output

local file = assert(io.open(filename, "w"))
file:write("something")
file:flush()
file:close()

An example of Reading a file

local BUFSIZE = 2^5
local filePath = "C:\\lua Programming\\wordcount.txt"
local file = io.input(filePath)
local cc, lc, wc = 0, 0, 0  -- character line and word counts
for lines, rest in io.lines(arg[1], BUFSIZE, "*L") do
  if rest then lines = lines .. rest end
  cc = cc + #lines

  -- count words
  local _, t = string.gsub(lines,"%S+","")
  wc = wc + t
  -- count newlines
  _, t = string.gsub(lines,"\n","\n")
  lc = lc + t

end
file:close()
print(lc, wc, cc)

References

The complete I/O model
The simple I/O model
TutorialsPoint Tutorial on IO

Table Library

table.insert(table, index, element) or insert(table, element)
   inserts an element into the table at index or at the end if index is not given
table.remove(table, index)
   removes the element at index from the table
   returns the element
table.sort(theTable)
   sorts the table by in order by index
table.concat(theTable) 
   for strings concatenates the table entries 
   returns a string

String Library

Some of the commonly used string functions are given below

string.len(s) returns the length of a string
string.rep(s, n) returns a string repeated n times
string.lower(s) returns the string in lower case
string.upper(s) converts to upper case
string.sub(s,i,j) returns a portion of the string from index i to j
string.byte(s, i) returns the numeric representation of the ith character of the string
string.byte(s, i, j) returns the numeric representation of the ith to jth characters
string.char(number) returns the character representation of the number in ASCII

For a complete listing see the references in the Contents
and get the Short Reference or a Reference Card

Patterns

  • Lua does not implement full regular expressions.
    • It implements a smaller subset.

See the reference card or short reference for the pattern matching syntax

Pattern Matching

string.find - searches for a pattern inside a given string
    string.find(originalString, pattern)
    returns the starting index and ending index of the matched portion

string.match - searches for a pattern inside a given string
    string.match(originalString, pattern)
    returns the portion of a string that matches a pattern

string.gsub - substitutes a replacement string for a matching string
    string.gsub(originalString, pattern, replacementString, numberOfSubstitutions)
    returns a new string with the replacements and the total number of replacements made

string.gmatch - iterates over all occurrences of a pattern in a string
    string.gmatch(originalString, pattern)
    returns the iterator, can be used with the generic for loop

An example using string.match

local theString = "This is the test string that will be used to work with patterns in Lua.  This should be interesting"

print(string.match(theString,"t[%a%s]+g"))

results are: the test string

  • The pattern was "t[%a%s]+g"
    • Starts with character t
    • has one or more letter or whitespace
    • ends with character g

An example using gsub

local newString = string.gsub(theString,"[tT][eshia]+[st]","zonk")
print(newString)

results are: zonk is the zonk string zonk will be used to work with patterns in Lua. zonk should be interesting

  • The pattern was [tT][eshia]+[st]
    • The starting character is t or T
    • One or more letters or spaces follow
    • The ending character is s or t
  • Replace all words matching the pattern with the word "zonk"

An example using string.gmatch

for word in string.gmatch(theString,"[tT][eshia]+[st]") do
   print(word)
end

results are: This test that This

  • The pattern was [tT][eshia]+[st]
    • The starting character is t or T
    • One or more letters or spaces follow
    • The ending character is s or t

Capture

Parentheses are used for capture in patterns

An example of capture in a pattern using gmatch

theString = "This is the test string that will be used to work with patterns in Lua.  This should be interesting"

-- work with capture
print("\n\n")
for word in string.gmatch(theString,"[tT]([eshia]+)[st]") do
   print(word)
end

results are: hi es ha hi

  • Captures only the letters between the start and end letters

Exercise using Strings and Pattern Matching (in teams of three)

Use the following text for this exercise (use a long string to input it or read it from a file)

"Cheese is a food derived from milk that is produced in a wide range of flavors, textures, and forms by coagulation of the
milk protein casein. It comprises proteins and fat from milk, usually the milk of cows, buffalo, goats, or sheep. During
production, the milk is usually acidified, and adding the enzyme rennet causes coagulation. The solids are separated and
pressed into final form.[1] Some cheeses have molds on the rind or throughout. Most cheeses melt at cooking temperature.

Hundreds of types of cheese from various countries are produced. Their styles, textures and flavors depend on the origin of
the milk (including the animal's diet), whether they have been pasteurized, the butterfat content, the bacteria and mold, the
processing, and aging. Herbs, spices, or wood smoke may be used as flavoring agents. The yellow to red color of many cheeses,
such as Red Leicester, is produced by adding annatto. Other ingredients may be added to some cheeses, such as black pepper,
garlic, chives or cranberries.

For a few cheeses, the milk is curdled by adding acids such as vinegar or lemon juice. Most cheeses are acidified to a lesser
degree by bacteria, which turn milk sugars into lactic acid, then the addition of rennet completes the curdling. Vegetarian
alternatives to rennet are available; most are produced by fermentation of the fungus Mucor miehei, but others have been
extracted from various species of the Cynara thistle family. Cheesemakers near a dairy region may benefit from fresher, lower
priced milk, and lower shipping costs."

Do the following:
1. Replace all of the words "cheese" with the work "air"
2. Replace all of the words that begin with an "m" and end with a "k", "r" or "i" with the word "boom"
3. Make three or more other replacements that would be interesting using complex patterns
   (Not simple word patterns)
4. Decide with those around you which is the best modification of the text
5. The best modification of the text should be shared with the class

The Operating System Library

os.time() - returns the current date and time as a number in seconds
os.time(table) - returns the date number represented by the table
    i.e.  os.time{year=2001, month=9, day=5, hour=14, min=7, sec=54}

os.date(format, time) can format the date as a string or put it into a table

An example using time and date

print(os.time{year=2001, month=9, day=5, hour=14, min=7, sec=54})
print(os.time({year=2001, month=9, day=5, hour=14, min=7, sec=54}))

local theTime = os.time({year=2001, month=9, day=5, hour=14, min=7, sec=54})

print(os.date("%B %w, %Y ",theTime))

Compilation

  • Lua precompiles code when it is run
  • Precompiled code can also be distributed
    • Precompiling is done using luac
    • These are called binary chunks

Garbage collection

  • Lua uses garbage collection.
    • It uses a mark and sweep garbage collector
    • The collector as of version 5.0 runs interleaved with the interpreter

Mark and Sweep

Mark and sweep performs garbage collection is three stages: mark, cleaning, and sweep
Mark - all reachable objects are marked as alive
Cleaning - Looks at all objects with a finalizer looking for non-marked objects.  These
    are put into a separate list to be handled.  It also looks at all weak tables and
    eliminates any entries that are not marked.  Weak tables are discussed below
Sweep - all objects are sweeped and those not marked are collected.  Lua also calls
    the finalizers of any objects put in the finalizer list

Weak Tables

  • Tables can have entries that are used by multiple other objects
    • They can have objects as keys or values
  • The objects referencing the table eventually get garbage collected
    • How to garbage collect the entry once it is no longer referenced
The __mode metamethod is used to declare that the keys or the values or both are weak

setmetatable(results, {__mode = "k"}) -- make the values weak
setmetatable(results, {__mode = "v"}) -- make the keys weak
setmetatable(results, {__mode = "kv"}) -- make both weak

If the values or keys are weak once all references to them are gone in the code
The garbage collector will not mark them as having references during mark
They will then be collected during cleaning

Finalizers

  • Tables to be destroyed can have entries in multiple places
    • In this case a finalizer can be provided on the table by implementing the __gc metamethod

Lua bytecode and virtual machine

  • The Lua compiler luac converts Lua code to bytecode
  • The Lua interpreter runs the bytecode

For those that are interested here are some references to explore References

The Lua VM Instruction Set
The Lua Compiler

Calling C modules from Lua

  • To call C from Lua is fairly easy
    • Just put the correct requires statement into the code
  • It is much harder to get a useful set of extensions in C and
    • Install them
    • Set up environment variables correctly
    • Configure Lua Development Tools (Eclipse) correctly

LuaRocks

  • LuaRocks is one of the main distribution methods for C code callable from Lua
    • LuaRocks that are useful to extend Lua
      • Networking
      • Database
      • JSON
      • and many others

References

LuaRocks.org website

Exercise Setting up LuaRocks within LDT

  • The Lua Development Tools (LDT) is the Eclipse environment for Lua
  • There are a number of steps required to setup a LuaRock with LDT
    • A C compiler is required
    • Make is required
    • LuaRock executable must be downloaded and "make" run to install it
    • Environment variables for LUA_PATH and LUA_CPATH must be set up
    • Mingw should be installed on a Windows computer (makes it easier)
    • A version of Lua interpreter must be installed
    • The appropriate LuaRoak must be downloaded and installed using LuaRocks executable
    • LDT must be configured with a new interpreter and an Execution Environment

The following PDF is a walk through of this process that installs LuaRocks and LuaSockets

  • LuaSockets is a socket library written in C used by Lua
Installing LuaRocks and LuaSockets

References on Software used for Lua and C

Lua Development Tools (Eclipse)
Eclipse for C
MinGW mingw-get-setup.exe
Lua 5.1.5 lua-5.1.5.tar.gz
LDT Execution Environment
LuaRocks luarocks-2.3.0-rc2-win32.zip
LuaSocket luasocket-master.zip

Networking with Coroutines Example using LuaSockets

To use networking a networking rock must be installed

  • Luasockets is a good one to use
    • To bring the code into Eclipse a local Lua interpreter must be set up
  • The steps to install luasockets for use in Eclipse are the following
    • Install a Lua 5.1 interpreter on the computer
      • LuaRocks seems to want a 5.1 interpreter, although a 5.2 should be tested to see if it works
    • Update the environment variables
    • Install LuaRocks on the computer
    • Update the environment variables
    • Install luasockets using LuaRock
      • They will be installed in the local interpreter folder that was installed above
    • Make the local Lua interpreter the project interpreter for a networking project in Eclipse
    • Install in Eclipse an Execution Environment that has the docs for luasockets for auto-completion
    • Code the project and try it
socket = require 'socket'

local host = "www.w3.org"
local threads = {} -- declare the table in which to put ids of coroutines that yield

function download (host, file)
  local connection = assert (socket.connect(host, 80))
  local count = 0
  connection:send("GET " .. file .. " HTTP/1.0\r\n\r\n")
  while true do
    local data, status = receive(connection)
    count = count + #data
    if status == "closed" then break end
  end
  connection:close()
  print (file, count)
end

function receive (connection)
  connection:settimeout(0)
  local data, status, partialData = connection:receive(2^10)
  if status == "timeout" then
    coroutine.yield(connection)
  end
  return data or partialData, status
end

function get (host, file)
  local co = coroutine.create(
    function ()
      download(host, file)
    end)

  table.insert(threads,co)
end

function dispatch()
  local i = 1
  local timedout = {}  -- table will hold threads that timed out so they can be restared
  while true do
    if threads[i] == nil then  -- at end of threads list?
      if threads[1] == nil then break end  -- are all threads finished?
      i = 1   -- restart at first thread
      timedout = {}  -- reset the timedout table to go through them again
    end
    local status, res = coroutine.resume(threads[i])
    if not res then  -- thread finished its task?
      table.remove(threads,i) -- remove from the table and change table size
    else
      i = i +1
      timedout[#timedout + 1] = res -- thread timedout so put it in timedout table
      if #timedout == #threads then  -- are all threads blocked?
        socket.select(timedout) -- if so then wait until the state of one of them changes
      end
    end
  end
end

-- now start up several coroutines to get pages over the web

get(host, "/Consortium/")
get(host, "/participate/")
get(host, "/")
get(host, "/Consortium/membership")
dispatch() -- start the main loop in the dispatcher

Exercise on LuaSockets

   1. Get the prior code on Coroutines and LuaSockets running
   2. Trace though the code using the debugger
   3. Prepare the following for discussion
       What is the purpose of the line connection:send("GET " .. file .. " HTTP/1.0\r\n\r\n")  ?
       What sets up the connection to the server.
       How does the code synchronize using coroutines?
       Discuss the relationship between coroutines and threads.
       What is the : after the connection identifier signify?
       Discuss the purpose of the threads table
   4. Implement the following
      Get the HTML of the page retrieved as well as the size of page and display both in the console

Create C Modules, Call from Lua

First a C module must be coded with functions to be called from Lua

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static int l_sin(lua_State *L) {
	double d = lua_tonumber(L, 1);
        //here is all of the code
	lua_pushnumber(L, sin(d));
	return 1; /*number of results*/
}

static const struct luaL_Reg mylib[] = { { "sin", l_sin }, { NULL, NULL } /*sentinel*/
};

int luaopen_mylib(lua_State *L) {
	//luaL_register(L, "mylib", mylib); /* lua 5.1*/
	luaL_newlib(L, mylib);
	return 1;
}
  • All functions must get one parameter a pointer to lua_State
    • Usually called L
  • Functions return the number of values that they placed on the Lua stack
  • Values are placed on the stack using lua_pushnumber etc.
  • A struct of type luaL_Reg is created which has the for of an array of name, function pointer pairs
    • The array last member must end in NULL, NULL as a sentinel
  • The struct is passed into the luaL_newlib function along with the lua_State
    • The registers the functions so they can be used by lua.
  • The program is compiled and built into a dll.
  • The dll is placed on the known lua cpath so that it can be found
  • The dll must have the same name as the module (what goes into the require statement)
  • The dll must be placed in a valid location on the lua cpath in the environment variables

References

Lua Reference Index of Function

Calling Lua from C

  • Lua can be called from C
  • The usual steps are:
    • Start a new Lua state
    • Open any desired Lua standard libraries
    • Push a function and parameters onto the "Stack"
    • Execute the function
    • Pop results from the stack
    • close the Lua state

An example of calling Lua from C

#include <stdio.h>
    #include <string.h>
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
    
    
    int main (void) {
      char buff[256];
      int error;
      lua_State *L = luaL_newstate();   /* opens Lua */
      luaL_openlibs(L);             /* opens the standard libraries */

    
      while (fgets(buff, sizeof(buff), stdin) != NULL) {
        error = luaL_loadstring(L, buff) || lua_pcall(L, 0, 0, 0);
        if (error) {
          fprintf(stderr, "%s\n", lua_tostring(L, -1));
          lua_pop(L, 1);  /* pop error message from the stack */
        }
      }
    
      lua_close(L);
      return 0;
    }
  • To make the above code run the following steps are needed
    • Download an Eclipse C programming IDE
    • Install a C compiler on Windows it is easiest to use Minimalist GNU for Windows (mingw)
    • Configure Eclipse so that it uses an installed C compiler (i.e. mingw gcc)
    • Create a project
    • Configure the project so that it uses the libLua.a library
      • By setting project Properties/C C++ General/Paths and Symbols/Library Paths/Add
      • Add the liblua.a library in Paths and Symbols/library add it as lua (remove lib and a)
    • Configure the project with the correct C header files by copying and pasting them
      • Or by specifying the path to them in Paths and Symbols
    • Create the source code and build it
    • Run the project


References

How to compile C with Lua Libraries
Lua 5.1 Reference Manual

The Stack

  • A stack is used to interface between C and Lua
  • Values and functions can be pushed onto it and then executed

Various Stack Operations are Shown in the Code Below

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void stackDump(lua_State *L) {
	int i;
	int top = lua_gettop(L); /* get the stack depth */
	for (i = 1; i <= top; i++) {
		int t = lua_type(L, i);
		switch (t) {
		case LUA_TSTRING: { /* strings */
			printf("'%s' ", lua_tostring(L, i));
			break;
		}
		case LUA_TBOOLEAN: { /* boolean */
			printf(lua_toboolean(L, i) ? "true " : "false "); /* print them as strings */
			break;
		}
		case LUA_TNUMBER: { /* number */
			printf("%g ", lua_tonumber(L, i));
			break;
		}
		default: { /* other values */
			printf("%s ", lua_typename(L, t));
			break;
		}
		}

	}
}

int main(void) {

	lua_State *L = luaL_newstate(); /* opens Lua */

	lua_pushboolean(L, 1); /* item 1 also item -4 on the stack after 4 pushes */
	lua_pushnumber(L, 10);
	lua_pushnil(L);
	lua_pushstring(L, "hello");

	stackDump(L);
	printf("%s", "\n");

	lua_pushvalue(L, -4);
	stackDump(L); /* push a copy of element at index -4 */
	printf("%s", "\n");

	lua_replace(L, 3);
	stackDump(L); /* get the top and put it at index 3 */
	printf("%s", "\n");

	lua_settop(L, 6);
	stackDump(L); /* set the stack size to 6, put nils */
	printf("%s", "\n");

	lua_remove(L, -3);
	stackDump(L); /* remove the item at -3 */
	printf("%s", "\n");

	lua_settop(L, -5);
	stackDump(L); /* pop -()n -1 or 4 elements from the stack */
	printf("%s", "\n");

	return 0;
}

Exercise on the Stack

Perform the following operations on the Lua Stack
   lua_pushnumber(L, 5.678);
   lua_pushboolean(L, 0);
   lua_pushvalue(L, -2);
   lua_pushnil(L);
   lua_remove(L, 1);
   lua_insert(L, -2)

Determine what the resulting stack should contain
Check your answer with stackDump

Accessing Lua Tables from C

  • Tables in Lua are accessed through the stack
    • Generally a Lua file is loaded and then the stack accessed
The rules for accessing a table are as follows:
  1. The assumption is that the table is global in Lua and can be accessed using
     lua_getglobal(L, tablename);
  2. lua_getglobal will cause the table to be put on the stack
  3. test that the top element on the stack is a table
  4. push a table key onto the stack, ends up on the stack top
  5. do a lua_gettable which will take the key and access the table
  6. do a lua_pop of the top element to remove the key
  7. do the same thing for other keys

An Example of accessing a table in Lua through the Stack

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

void load (lua_State *L, const char *fname, int *red, int *green, int *blue);
int getcolorfield(lua_State *L, const char *key);

#define MAX_COLOR 255
int main(void) {
    lua_State *L = luaL_newstate();   /* opens Lua */
    luaL_openlibs(L);             /* opens the standard libraries */
    char filename[] = "C:/Lua C and C++/workspace/configTable.lua";
    int red = 0;
    int green = 0;
    int blue = 0;
    load(L, filename, &red, &green, &blue);
    printf("red is %d\n", red);
    printf("green is %d\n", green);
    printf("blue is %d", blue);
    return 0;
}

void load (lua_State *L, const char *fname, int *red, int *green, int *blue) {
	if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) {
		luaL_error(L, "cannot run config file: %s", lua_tostring(L, -1));
	}
	lua_getglobal(L, "background");
	if (!lua_istable(L, -1)) {
			luaL_error(L, "'background' is not a table\n");
	}
	int r = getcolorfield(L, "r");
	int g = getcolorfield(L, "g");
	int b = getcolorfield(L, "b");

	*red = r;
	*green = g;
	*blue = b;
}

int getcolorfield(lua_State *L, const char *key) {
	int result;
	lua_pushstring(L, key);
	lua_gettable(L, -2);
	if (!lua_isnumber(L, -1)) {
		luaL_error(L, "invalid component in background color");
	}
	result = (int)(lua_tonumber(L, -1) * MAX_COLOR);
	lua_pop(L, 1);
	return result;
}

The Lua file with the table

background = {r=0.30, g=0.10, b=0}
BLUE = {r=0, g=0, b=1.0}
background1 = BLUE

Accessing Functions through the Stack

  • Functions from a lua file can be accessed through the stack
    • The file with the functions is loaded first
The basic steps to access a function are the following
  1. Load the file with the function
  2. Use lua_getglobal(L, "functionName") to load the function reference onto the stack
  3. Push the arguments onto the stack with the first argument going on first etc.
     Note that this means that the first argument is the furthest down on the stack
     if several arguments are being passed
  4. Call lua_pcall(L, ParameterNum, resultsNum, 0) which executes the function and
     pushes the results onto the stack
  5. Test that the correct result types are on the stack using lua_isnumber etc.
  6. Get the results from the stack using lua_tostring etc.
  7. Pop the stack using lua_pop to put the function and arguments from the stack

An example of the code to execute a Lua function from C code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"


#define MAX_COLOR 255
char *load (lua_State *L, const char *fname, int *number, int x, int y, char* myString);

int main(void) {
    lua_State *L = luaL_newstate();   /* opens Lua */
    luaL_openlibs(L);             /* opens the standard libraries */
    char filename[] = "C:/Lua C and C++/workspace/functionFile.lua";
    int number = 0;
    int x = 5;
    int y = 6;
    char *a = malloc(100);
    strcpy(a, "Hello to ");
    char * resultString = load(L, filename, &number, x, y, a);

    printf("number is %d\n", number);
    printf("The String is %s\n",  resultString);
    free(a);
    free(resultString);
    return 0;
}


char *load (lua_State *L, const char *fname, int *number, int x, int y, char* myString) {


	if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) {
		luaL_error(L, "cannot run config file: %s", lua_tostring(L, -1));
	}

	lua_getglobal(L, "modifyData");
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);
	lua_pushstring(L, myString);

	if (lua_pcall(L, 3, 2, 0) != 0) {
		luaL_error(L, "error in function ");
	}

	int z = lua_tointeger(L, -2);
    printf("the number is %d\n", z);
    *number = z;

    myString = lua_tostring(L, -1);
	printf("the string is %s\n", myString);

	lua_pop(L, 1);
	lua_pop(L, 1);
	return myString;
}

The Lua file with a function looked like the following

function modifyData(x, y, theString)
   theString = theString .. "the world"
   return x*y , theString
end


  • Errors can be handled either by the C code or by calling LuaL_error(L, errorMessage);

Continuations

References

Lua Reference Manual

Memory management

   Allocators
   GC API

Threads in Lua

  • Lua does not support multithreading because of:
    • Performance penalties related to synchronization, blocking, and deadlocking
    • Hard to find bugs related to threads
    • Each operating systems has varying threading and synchronization primitives
  • Co-routines are provided instead
  • Threading can be done through C
    • However the coding and results are then operating system dependent

Sample Lua and C code

Lua Sample Code for Eclipse LDT
C Code using Eclipse that interacts with Lua