Exploring Lua's Libraries - Beginning Lua Programming (2007)

Beginning Lua Programming (2007)

Chapter 11. Exploring Lua's Libraries

Up to this point in the book you've been learning about the fundamentals of Lua, occasionally using some of its library functions. In this chapter, all of Lua's standard library functions are briefly summarized to remind you of their basic usage. Practically every function has many more features than can be covered in detail within the scope of this book, and the examples that are included here are mostly contrived for brevity. (For the definitive documentation of these features, see the Lua Reference Manual.)

In this chapter, you'll find summaries of the following:

· General-purpose functions in the core library

· Functions that manage coroutines

· Package functions that implement Lua's module system

· String conversion and pattern matching functions

· Functions that help with using tables as arrays

· Bindings to the C runtime library's math functions

· Functions for reading from and writing to files and streams

· Operating system functions

· Functions to help with debugging your Lua programs

An example is provided for each function or group of functions.

Core Library

The core library functions reside in the global environment. They have no containing namespace table.

Environment Functions

The following functions let you set and retrieve a function's environment—the table that Lua uses to resolve global references—and get or set the global environment of the running thread when given a stack level of 0:

· getfenv (optional function or stack level): Returns the environment table of the given function, or the function running at the given stack level. The argument defaults to 1, which is the stack level of the current function (the one calling getfenv).

· setfenv (function or stack level, environment table): Gives an environment table to the given function or the function at the given stack level, and returns the function.

This example demonstrates how you can set a global environment for a particular function:

Str = "Global"

function Sandbox()

print(getfenv(), Str)

end

setfenv(Sandbox, {print = print, getfenv = getfenv, Str = "Sandbox"})

Sandbox()

print(getfenv(), Str)

The output generated by the example shows how the value of the global variable Str depends on the environment:

table: 00765740 Sandbox

table: 00760300 Global

Metatable Functions

These functions let you set, retrieve, and circumvent metatables, the mechanism by which you can modify the default behavior for various operations:

· getmetatable(value): Returns its argument's__metatable metamethod, its argument's metatable if there is no__metatable metamethod, or nil if there is no metatable.

· setmetatable(table, metatable): Gives a metatable to a table. An error will be triggered if the table has a __metatable metamethod.

· rawequalfrst(first value, second value): Returns true if the two values are equal, bypassing any metamethods.

· rawget(table, index): Retrieves table[index], bypassing any metamethods.

· rawset(table, index, value): Assigns a value to table[index], bypassing any metamethods.

This example shows how you can use the__eq metamethod to modify the way that the equality operator works for tables:

MtTbl = {__eq = function(T1, T2) return T1.Val == T2.Val end}

A = {Val = 10}

setmetatable(A, MtTbl)

B = {Val = 10}

setmetatable(B, MtTbl)

C = {Val = 12}

setmetatable(C, MtTbl)

print(MtTbl, getmetatable(A), getmetatable(B), getmetatable(C))

print("A == B", A == B)

print("A == C", A == C)

print("rawequal(A, B)", rawequal(A, B))

print("rawequal(A, C)", rawequal(A, C))

The example generates the following output, demonstrating that the_eq metamethod is engaged by the equality operator and bypassed with the rawequal function:

table: 00765530 table: 00765530 table: 00765530 table: 00765530

A == B true

A == C false

rawequal(A, B) false

rawequal(A, C) false

This example illustrates how extra functionality can be attached to the assignment and retrieval of a table's field.

local function WindowCreate(TitleStr)

local function WindowTextGet()

-- Simulate GUI call

io.write(" Retrieving ", TitleStr, "\n")

return TitleStr

end

local function WindowTextSet(Str)

-- Simulate GUI call

local OldTitleStr = TitleStr

io.write(" Setting ", Str, "\n")

TitleStr = Str

return OldTitleStr

end

local function MtIndex(Tbl, Key)

return Key == "Title" and WindowTextGet() or Tbl[Key]

end

local function MtNewIndex(Tbl, Key, Val)

if Key == "Title" then

WindowTextSet(Val)

else

Tbl[Key] = Val

end

end

local Wnd, MtTbl = {}, {__index = MtIndex, __newindex = MtNewIndex}

setmetatable(Wnd, MtTbl)

return Wnd

end

local Wnd = WindowCreate("Title 1")

local Str = Wnd.Title

io.write("1 ", Str, "\n")

Wnd.Title = "Title 2"

Str = Wnd.Title

io.write("2 ", Str, "\n")

rawset(Wnd, "Title", "Title 3")

Str = rawget(Wnd, "Title")

io.write("3 ", Str, "\n")

Wnd.Title = "Title 4" -- No metamethod

Str = Wnd.Title -- No metamethod

io.write("4 ", Str, "\n")

The output of the example shows how metamethods can define new behavior for indexing and assignments operations. Note that the use of rawset creates a field named Title in the Wnd table, effectively disabling the use of metamethods for subsequent indexing and assignments:

Retrieving Title 1

1 Title 1

Setting Title 2

Retrieving Title 2

2 Title 2

3 Title 3

4 Title 4

Chunk-Loading Functions

The chunk-loading functions generate a function from a Lua chunk. The chunk can be in either source or precompiled form. Specifically, each of these functions does the following:

· dofile (optional name of file): Executes the specified Lua script, or standard input if the filename is missing.

· load (loader function, optional chunk name): Repeatedly calls the loader function to build up a Lua chunk. Each time the loader function is called it returns the next portion of the Lua script in the form of a string. When the entire script has been loaded, the loader function must return nil, at which point load compiles the chunk and returns the function without running it.

· loadfile (optional name of file): Loads the specified file as a Lua chunk and returns the compiled function without running it.

· loadstring (script string, optional name): Loads the specified string as a Lua chunk and returns the compiled function without running it.

The following example demonstrates the preceding functions with a simple Lua chunk that simply prints the arguments that are passed to it. The first iteration of the loop loads the chuck in source form, the second in precompiled form:

local FileStr = "test.lua"

for J = 1, 2 do

local LuaStr

if J == 1 then

io.write("=== Source chunk ===\n")

LuaStr = "print('source', ...)"

else

io.write("=== Compiled chunk ===\n")

LuaStr = string.dump(function(...) print('compiled', ...) end)

end

local Hnd = io.open(FileStr, "wb")

if Hnd then

Hnd:write(LuaStr)

Hnd:close()

io.write("[dofile] ")

dofile(FileStr)

io.write("[loadfile] ")

local Fnc, ErrStr = loadfile(FileStr)

if Fnc then

Fnc(1, 2, 3)

io.write("[loadstring] ")

Fnc, ErrStr = loadstring(LuaStr)

if Fnc then

Fnc(1, 2, 3)

io.write("[load] ")

local RetStr = LuaStr

local function Loader()

local Ret = RetStr

RetStr = nil

return Ret

end

Fnc, ErrStr = load(Loader)

if Fnc then

Fnc(1, 2, 3)

else

io.write(ErrStr, "\n")

end

else

io.write(ErrStr, "\n")

end

else

io.write(ErrStr, "\n")

end

else

io.write("Error opening ", FileStr, " for writing\n")

end

end

os.remove(FileStr)

The example generates the following output:

=== Source chunk ===

[dofile] source

[loadfile] source 1 2 3

[loadstring] source 1 2 3

[load] source 1 2 3

=== Compiled chunk ===

[dofile] compiled

[loadfile] compiled 1 2 3

[loadstring] compiled 1 2 3

[load] compiled 1 2 3

Note that chunks executed with dofile can't accept arguments. Note also that all of the functions handle source chunks and precompiled chunks correctly.

Error-Containment Functions

These functions are used to keep runtime errors from propagating toward the host program:

· pcall (function, optional arguments) : The specified function is called with the specified arguments. In the event of an error, a one-line message is generated.

· xpcall (function, error handler): The specified function is called. In the event of an error, the specified error handler is invoked before the stack is reset, allowing a stack traceback to be constructed. The handler is passed an error string and returns an augmented error string.

If the specified function runs without errors, both pcall and xpcall return true followed by the function's own return values. If an error occurs, false is returned followed by the error message.

This example shows how a runtime error can be caught and managed within a Lua program.

io.write("Start of tests\n")

io.write("-- pcall --\n")

local BadLuaStr = "print('Intentional error ahead', ...) error('crash')"

Fnc, ErrStr = loadstring(BadLuaStr, "Demonstration")

if Fnc then

local Code, ErrStr = pcall(Fnc, "pcall")

if not Code then

io.write("[pcall error] ", ErrStr, "\n")

end

io.write("-- xpcall --\n")

local Code, ErrStr = xpcall(Fnc, debug.traceback)

if not Code then

io.write("[xpcall error] ", ErrStr, "\n")

end

else

io.write("[loadstring error] ", ErrStr, "\n")

end

io.write("End of tests\n")

The example generates the following output, showing that the script recovers from both errors:

Start of tests

-- pcall --

Intentional error ahead pcall

[pcall error] [string "Demonstration"]:1: crash

-- xpcall --

Intentional error ahead

[xpcall error] [string "Demonstration"]:1: crash

stack traceback:

[C]: in function 'error'

[string "Demonstration"]:1: in main chunk

[C]: in function 'xpcall'

test.lua:11: in main chunk

[C]: ?

End of tests

Module Functions

These functions are Lua's means to modularize your programs:

· module (module name, zero or more preparation functions): Modularizes a script, storing all new global variables in a namespace table. This table will be assigned to a global variable with the specified module name. Any functions that are specified are called in turn with the namespace table as an argument. The package.seeall function is commonly used in this capacity.

· require (module name): Loads and executes the specified Lua file following a well-defined protocol.

See the package library functions for related functions.

The following example generates a little module that simply assigns a greeting to a string, and then loads the module using Lua's module system:

io.output("testmod.lua")

io.write [[module(...)

Str = "Hello from " .. (...)

]]

io.close()

io.output(io.stdout)

package.path = "?.lua"

require("testmod")

io.write(testmod.Str, "\n")

The following output from the example shows that the module is successfully loaded:

Hello from testmod

The Garbage-Collection Function

Garbage collection refers to the automatic reclamation of memory used by Lua values that can no longer be accessed by your program. It has one function:

collectgarbage("collect" or "count" or "restart" or "setpause" or "setstepmul"

or "step" or "stop", optional extra argument)

This function performs one of actions described in the following table based on the value of the first argument.

11-1

The following example shows the effect of collecting garbage after creating and then releasing a large string:

local function KbUsed(Seq)

io.write(string.format("%2d %8.2f KB\n", Seq, collectgarbage("count")))

end

KbUsed(1)

Str = string.rep("*", 1e5)

KbUsed(2)

Str = nil

collectgarbage("collect")

KbUsed(3)

collectgarbage("collect")

KbUsed(4)

collectgarbage("collect")

KbUsed(5)

The example prints the following lines, showing how successive garbage collection cycles reclaim memory:

1 21.15 KB

2 324.48 KB

3 44.19 KB

4 25.88 KB

5 21.30 KB

Type and Conversion Functions

These functions let you identify types and convert between them:

· tonumber (value, optional numeric base from 2 to 36): Converts the specified value to a number using the specified base. The value must represent an unsigned integer if the base is other than 10. For base 10, the default, the value can represent a floating point number.

· tostring (value): Returns a string representation of the specified value, using the ___tostring metamethod if one is registered.

· type (value): Returns a string representation of the specified value's type. This will be "nil", "boolean", "number", "string", "table", "function", "thread" or "userdata".

The following example demonstrates the conversion of a binary number to decimal:

BinStr = "101010"

Num = tonumber(BinStr, 2)

DecStr = tostring(Num)

io.write(BinStr, " (", type(BinStr), "), ", Num, " (", type(Num), "), ",

DecStr, " (", type(DecStr), ")\n")

The example prints the following:

101010 (string), 42 (number), 42 (string)

Basic Output

This category provides a quick way for Lua programs to print values to standard output. The print (zero or more values) function writes the string equivalent of each argument to standard output. A tab is written after each argument except the last in which case a newline is written.

This example prints an assortment of values to the standard output stream:

print(nil, 1 == 1, 1 + 1, "one", function() end, {})

The output shows how various values are rendered by the print function:

nil true 2 one function: 003D9490 table: 003DA478

Error-Condition Functions

These functions conditionally or unconditionally cause an error to propagate toward the host program at runtime. The most recent protected call will contain the error and prevent it from propagating further.

Here are the error-condition functions and examples of each:

· assert (test expression, optional error message): If the test expression evaluates to false or nil, an error is raised with the specified error message or, if it is missing, with the text assertion failed! For example, the following tests the assumption of a value's type:

Val = 12

assert(type(Val) == "boolean", "Expecting boolean value")

This outputs the following stack traceback when the assertion expression evaluates to false:

lua: assert.lua:2: Expecting boolean value

stack traceback:

[C]: in function 'assert'

assert.lua:2: in main chunk

[C]: ?

· error (error message, optional “skip” level): Unconditionally causes an error using the specified message. The second argument specifies the number of stack frames to skip when reporting the error's origin. This is useful when writing a custom error handler. This example issues an error:

error("Printer on fire.")

The output of the example uses the Lua interpreter's default error handler:

lua: err.lua:1: Printer on fire.

stack traceback:

[C]: in function 'error'

error.lua:1: in main chunk

[C]: ?

Table Traversal Functions

These functions let you visit each key-value pair in a table:

· ipairs (array): Can be used in a generic for loop to visit all array fields in the specified table. These are values whose keys are contiguous integers beginning with 1.

· next (table, optional seed key): Returns the next key-value pair from the table's internally ordered sequence. The first key and value from this sequence are returned when the key argument is omitted. The value nil is returned when the last key in the sequence is specified.

· pairs (table) : Can be used in a generic for loop to iterate over all key-value pairs in the table.

This example initializes a table with indexed and associative elements and iterates through the table using the table traversal functions:

Tbl = {[0] = "zero", "one", "two", Id = 100, Name = "Alexander Botts",

"three", [10] = "ten"}

io.write("--- ipairs ---\n")

for J, Val in ipairs(Tbl) do

io.write(J, ": ", tostring(Val), "\n")

end

io.write("--- pairs ---\n")

for Key, Val in pairs(Tbl) do

io.write(tostring(Key), ": ", tostring(Val), "\n")

end

io.write("--- next ---\n")

local Key, Val = next(Tbl)

while Key do

io.write(tostring(Key), ": ", tostring(Val), "\n")

Key, Val = next(Tbl, Key)

end

The example's output show how pairs and next can be used to visit all key-value pairs in a table, but ipairs traverses only array elements:

--- ipairs ---

1: one

2: two

3: three

--- pairs ---

1: one

2: two

3: three

0: zero

10: ten

Name: Alexander Botts

Id: 100

--- next ---

1: one

2: two

3: three

0: zero

10: ten

Name: Alexander Botts

Id: 100

Vararg-Related Functions

The following two functions are useful for working with vararg expressions ...), vararg functions, functions that return multiple values, and multiple assignment:

· select (position or "#", zero or more extra arguments): When the first argument is "#", this function returns the number of extra arguments. When the first argument is an integer, it returns the extra argument specified by that integer, and all of the following extra arguments.

· unpack (array, optional start position, optional end position): Returns the values (in order) in the given array. The start and end positions default (respectively) to 1 and the length of the array.

The following example demonstrates the use of select and unpack:

function Test(... )

io.write( "*** Entering Test ***\n ")

for J = 1, select( "# ", ...) do

io.write(select(J, ...))

io.write( "\n ")

end

end

List = {"One", "Two", "Three"}

Test(unpack(List))

Test(unpack(List, 1, 2))

Test(unpack(List, 2))

The example's output shows how unpack can be used to return a contiguous range of arrayed values, and how select can be used to obtain the elements in a vararg expression and their number:

*** Entering Test ***

OneTwoThree

TwoThree

Three

*** Entering Test ***

OneTwo

Two

*** Entering Test ***

TwoThree

Three

Coroutine Library

The following coroutine functions are used to cooperatively manage multiple threads in a Lua program:

· coroutine.create (function) : Creates a coroutine from the specified function. The returned value is of type thread.

· coroutine.resume (coroutine, optional values) : Activates a coroutine with any practical number and type of arguments. The values following the coroutine in the argument list are passed to the coroutine as follows. When the coroutine is initially activated, the arguments are conveyed in the coroutine body's argument list. When the coroutine is subsequently resumed, these arguments appear as return values from coroutine.yield. This function returns the values that are passed to coroutine.yield or are explicitly returned when the coroutine ends.

· coroutine.running() : Returns the currently running coroutine, or nil if the main thread is running.

· coroutine.status (coroutine): Returns a string with the value "running", "suspended", "normal" or "dead".

· coroutine.wrap (function) : Creates a coroutine from the specified function and returns a function wrapper to it. The coroutine is resumed by calling the returned function directly. This can make activating the coroutine simpler; however, runtime errors are not contained.

· coroutine.yield (zero or more values): Transfers control back to the caller of coroutine.resume that activated the current coroutine. The arguments are conveyed to the caller in the form of return values from coroutine.resume.

The following example shows how coroutines can be used to retain the state needed to generate a running average of numbers. Both the wrapped and unwrapped varieties of coroutines are demonstrated:

local function Average()

local Co = coroutine.running()

Val = coroutine.yield(tostring(Co) .. " is " ..coroutine.status(Co))

local Count, Sum = 1, Val

while true do

Sum = Sum + coroutine.yield(Sum, Sum / Count)

Count = Count + 1

end

end

local CoAve = coroutine.create(Average)

local Code, Str = coroutine.resume(CoAve)

if Code then

io.write(Str, "\n")

local Ave = coroutine.wrap(Average)

io.write(Ave(), "\n\n")

io.write("Val A Sum A Ave A Val B Sum B Ave B\n")

io.write("---- ----- ----- ----- ----- -----\n")

math.random() -- On some systems that shall remain nameless, the

-- first call to math.random is easy enough to predict that it

-- destroys the illusion of randomness; throw it away,

for J = 1, 6 do

local Code, ValA, SumA, AveA, ValB, SumB, AveB

ValA = math.random(0, 100)

Code, SumA, AveA = coroutine.resume(CoAve, ValA)

if Code then

ValB = math.random(0, 100)

SumB, AveB = Ave(ValB)

io.write(string.format("%5d %5d %5.2f %5d %5d %5.2f\n",

ValA, SumA, AveA, ValB, SumB, AveB))

end

end

end

The example's output shows identical results calculated for identical input to the averaging coroutines:

thread: 003DAD60 is running

thread: 003DB3B0 is running

Val A Sum A Ave A Val B Sum B Ave B

----- ----- ----- ----- ----- -----

56 56 56.00 19 19 19.00

81 137 68.50 59 78 39.00

48 185 61.67 35 113 37.67

90 275 68.75 83 196 49.00

75 350 70.00 17 213 42.60

86 436 72.67 71 284 47.33

Package Library

The following functions, strings, and tables in this library are used with or by module and require to implement Lua's package system:

· package.loadlib (library name, function name): Loads the specified library and returns a function bound to the specified C function in the library. Because ANSI C doesn't provide for loading libraries dynamically, this function is implemented in a platform-specific manner and is available only on mainstream systems. The following example demonstrates the direct loading of a shared library:

Usually a Lua program uses the require function instead of loadlib to take advantage of Lua's package searching and caching conventions.

Fnc, ErrStr = package.loadlib("./luabogo.so", "luaopen_bogo")

if Fnc then

Fnc("bogo")

else

io.write(ErrStr, "\n")

end

· package.seeall (namespace table) : Sets the metatable for a module namespace so that values in the global environment can be accessed. This is used as a preparation function with the module function. For example, the following creates a small module that specifies package.seeall as an argument to module so that it can access to the global environment. Note that package.path is modified so that the script looks for the module in the current directory:

io.output("testmod.lua")

io.write [[module(..., package.seeall)

local ModStr = (...)

function Hello()

io.write("Hello from ", ModStr, "\n")

end

]]

io.close()

io.output(io.stdout)

package.path = "?.lua"

require("testmod")

testmod.Hello()

The example's output shows how the module is able to call io.write as a result of using package.seeall:

Hello from testmod

· package.preload: Each key in this table is the name of a module, and the corresponding value is a function that is called to load the module. Lua's package mechanism checks this table for a module loading function before it searches for a file-based module.

· package.loaded: Each key in this table is the name of a loaded module, and the corresponding value is the value that was returned by the module's loading function. Lua's package mechanism checks this table when require is called to see if the module has already been loaded. If it has, the corresponding value is returned rather than loading the module again.

· package.path: This string consists of semicolon-separated segments that are used to locate a Lua module file. It is initially assigned the value of the environment variable LUA_PATH (or a default value from luaconf.h, if LUA_PATH is not defined). When searching for a module file, Lua replaces each question mark with the name passed to require.

· package.cpath: This string consists of semicolon-separated segments that are used to locate a compiled library. It is initially assigned the value of the environment variable LUA_CPATH (or a default value from luaconf.h, if LUA_CPATH is not defined). When searching for a library, Lua replaces each question mark with the name passed to require.

The following example demonstrates the use of the package.loaded and package.preloaded tables:

io.output("a.lua")

io.write('return {Str = "Module a (file)."}')

io.close()

io.output("b.lua")

io.write('return {Str = "Module b (file)."}')

io.close()

io.output("c.lua")

io.write('return {Str = "Module c (file)."}')

io.close()

io.output(io.stdout)

package.cpath = ""

package.path = "./?.lua"

function package.preload.a()

return {Str = "Module a."}

end

package.loaded.b = {Str = "Module b."}

local function Report(ModStr)

io.write('package.loaded["', ModStr, '"] = ',

tostring(package.loaded[ModStr]), '\n')

end

Report("a")

Report("b")

Report("c")

local A = require("a")

local B = require("b")

local C = require("c")

io.write("A: ", A.Str, "\nB: ", B.Str, "\nC: ", C.Str, "\n")

Report("a")

Report("b")

Report("c")

Three file-based modules are created, and package.cpath and package.path are set so that Lua will look for Lua modules only in the current working directory, and will not look for compiled libraries at all. A loader function is associated with “a” in the package.preload table. When the "a" module is required, Lua will call this function and return the value that it returns. A table is associated with "b" in the package.loaded table. When the "b" module is required, Lua returns this value directly. Because the "c" module doesn't have an entry in either of these tables, Lua searches for a file module. It does this by stepping through each semicolon-separated segment in package.path, replacing each ? with the name of the module. In this example, the one segment in package.path forms the name ./c.lua, and Lua successfully opens this file as a Lua chunk. The example outputs the following lines:

package.loaded["a"] = nil

package.loaded["b"] = table: 003DA7E8

package.loaded["c"] = nil

A: Module a.

B: Module b.

C: Module c (file).

package.loaded["a"] = table: 003DAEC8

package.loaded["b"] = table: 003DA7E8

package.loaded["c"] = table: 003DAFF0

String Library

The string library provides a versatile set of functions for manipulating text in Lua programs. Character positions may be positive or negative integers. A positive value indicates a position counted from the beginning of the string with the first character at position 1. A negative value indicates a position counted backwards from the end of the string, with the last character at position -1.

Pattern-Based String Functions

The following string functions use Lua's pattern matching facility to locate and extract character sequences in a string (see “Pattern Matching” in Chapter 5 for details about matching text):

· string.find (subect string, pattern string, optional start position, optional plain flag): Looks for a pattern in the specified subject string. The search is done with Lua's pattern-matching rules unless the optional plain flag is true, in which case a character-for-character search is done, with none of the characters in the pattern string having their magic meaning. The search begins at the beginning of the subject unless a start position is specified. If a match is found, the start and end positions are returned followed by any captures that are specified in the pattern string. For example, the following demonstrates the use of string.find with an incremented starting position to extract all alphanumeric sequences from a string:

local Str = "/var/local/www"

local PosStart, PosEnd, MatchStr = 1

while PosStart do

PosStart, PosEnd, MatchStr = string.find(Str, "(%w+)", PosStart)

if PosStart then

io.write(MatchStr, "\n")

PosStart = PosEnd + 1

end

end

The example prints the following lines.

var

local

www

· string.gmatch (subect string, pattern string): Iterator factory for looping through pattern matches in the subject string using a generic for loop. For example, the following is a more convenient method than the one in the previous example that you can use to extract matching character sequences from a string:

for Str in string.gmatch("CamelbackName", "%u%l+") do

io.write(Str, "\n")

end

The example prints the following string captures.Camelback

Name

· string.gsub (subect string, pattern string, replacement string or function or table, optional limit count) : Substitutes each pattern match found in the subject string with a replacement. The replacement argument can be a string (which can contain captures such as “%1”), or a function that is called for each match (with captures passed as arguments), or a table in which the value given by a matching key is used for the replacement. The number of replacements can be limited by specifying a numeric fourth argument. The following example shows how string.gsub can be used to modify the contents of a string:

-- Generate list of month names in current locale

local Dt = os.date("*t")

Dt.day = 1

local MonthList = {}

for Month = 1, 12 do

Dt.month = Month

MonthList[Month] = os.date("%B", os.time(Dt))

end

function ExpandDate(Month, Day, Sep)

return (MonthList[tonumber(Month)] or "---") .. " " ..

tonumber(Day) .. (Sep == "," and ", " or "")

end

local Str = "0117,0211,0818"

io.write("Before: ", Str, "\n")

Str = string.gsub(Str, "(%d%d)(%d%d)(%,?)", ExpandDate)

io.write("After: ", Str, "\n")

The example prints the following lines.

Before: 0117,0211,0818

After: January 17, February 11, August 18

· string.match (subect string, pattern string, optional start position): Returns the captures of the first match found in the subject. If no match is found, nil is returned. If a match is found but no captures are specified, the entire match is returned. For example, the following extracts the red, green and blue hexadecimal components of a color identifier:

local ClrStr = "#C6EFF7"

local Red, Green, Blue = string.match(ClrStr, "%#(%x%x)(%x%x)(%x%x)")

if Red then

io.write("Red: ", tonumber(Red, 16), ", Green: ", tonumber(Green, 16),

", Blue: ", tonumber(Blue, 16), "\n")

end

The following line is printed by the example:

Red: 198, Green: 239, Blue: 247

String-Conversion Functions

Lua's string-conversion functions return one or more values pertaining to the specified string or substring, as follows:

· string.byte (string, optional start position, optional end position): Returns the platform-dependent byte values associated with a range of text characters. If the start position is missing, 1 is used. If the end position is missing, the start position is used. For example, the following returns the byte values of the letters L, u and a:

print(string.byte("Lua", 1, 3))

The following values are printed by the example:

76 117 97

· string.char (zero or more integers): Constructs and returns a string comprising the characters associated with the platform-dependent argument values. For example, the following converts the specified numeric values to a string:

print(string.char(76, 117, 97))

The example prints the following string:

Lua

· string.dump (function): Returns a copy of the function converted to binary string form for storage or transmission. It can be restored with one of the load functions. (Officially, the function cannot have upvalues, but Chapter 10 describes what happens if it does.) The following example converts a simple Lua function to string form, and then creates a new function from the string and executes it with a number of arguments:

Str = string.dump(function(...) print(...) end)

print(string.gsub(Str, "%A+", "..."))

loadstring(Str)(1, 2, 3)

The example prints the following lines (the actual values shown in the first line may vary between platforms):

...LuaQ...sb...lua...E...print...arg... 7

1 2 3

· string.format (format string, zero or more values): Returns a single formatted string. The format string specifies how the arguments that follow it are to be formatted, following conventions close to those used by the printf function in the standard C library. For example, you could usestring.format to generate a hexadecimal color identifier like this:

io.write(string.format("Color: #%02X%02X%02X\n", 198, 239, 247))

The following line is printed by the example:

Color: #C6EFF7

· string.len (string): Returns the number of bytes in the specified string, including non-text characters such as "\0".

· string.lower (string) : Returns a copy of the specified string in which all uppercase letters are transformed to their lowercase values.

· string.rep (string, repeat count): Returns a string made up of the specified string repeated the specified number of times.

· string.reverse (string) : Returns a copy of the specified string in which the sequence of the characters is reversed.

· string.sub (string, start position, optional end position) : Returns the portion of the specified string indicated by the start and end positions.

· string.upper (string) : Returns a copy of the specified string in which all lower case letters are transformed to their upper case values.

This example demonstrates a number of conversions in which the returned string is a modification of the passed-in string:

local Str = "Lua"

io.write(string.format("Normal: '%s', reversed: '%s', " ..

"lower: '%s', upper: '%s'\n\n",

Str, string.reverse(Str), string.lower(Str), string.upper(Str)))

for J, TblStr in ipairs{"coroutine", "debug", "io", "math",

"os", "package", "string", "table"} do

local Count = 0

for FldStr in pairs(_G[TblStr]) do

Count = Count + 1

end

io.write(string.format("%s%s%d\n", TblStr, string.rep(".",

14 - string.len(TblStr) - string.len(Count)), Count))

end

The example prints the following lines:

Normal: 'Lua', reversed: 'auL', lower: 'lua', upper: 'LUA'

coroutine....6

debug.......14

io..........14

math........31

os..........11

package......8

string......15

table........9

Table Library

The table functions provide a means to work with tables as arrays (that is, tables with contiguous integer indexes beginning with 1). The table library contains the following functions:

· table.concat (array, optional separator string, optional start position, optional end position ): Returns a single string made up of the elements in the specified table, which must be strings or numbers. If a separator is specified, it will be used to delimit the elements. A range of elements can be specified using the start and end positions.

· table.insert (array, optional position, value): Inserts a value into the specified table. If the position is missing, the value is added to the end of the array, otherwise it's added to the indicated position. In either case, the number of values in the array is increased by one. If the insertion is into before the last position, elements are moved up to make room.

· table.maxn (array): Effectively performs a pairs loop to determine the largest positive numeric key in the specified table. The returned number may be larger than the value an ipairs loop would reach, and the number may be a noninteger. If there are no positive keys, 0 is returned.

· table.remove (array, optional position : Removes the table element associated with the specified position and returns its value. If the position is missing, the last element in the array is removed. Elements are moved down to fill the vacated position.

· table.sort (array, optional comparison function): Sorts the elements in the specified array. If a comparison function is specified, it will be called as needed with two table elements as arguments. It should return true if the first element is less than the second and false otherwise. If this function is not specified, < will be used to compare elements.

This example demonstrates the manipulation of a table:

Tbl = {"This is line 4", "This line is out of place", "This is line 2"}

table.insert(Tbl, "This is line 1")

table.remove(Tbl, 2)

table.insert(Tbl, "This is line 3")

table.insert(Tbl, "This is line " .. table.maxn(Tbl) + 1)

table.sort(Tbl)

io.write(table.concat(Tbl, "\n"), "\n")

The example prints the following lines:

This is line 1

This is line 2

This is line 3

This is line 4

This is line 5

Math Library

The functions in the math library are closely bound to functions of the same name in the standard C library. Plots of some of the functions are shown here—these were generated with Lua using the GD library (which you'll get a chance to experiment with in the “Producing a Function Plot” Try It Out in Chapter 12).

Trigonometric Functions

These functions return a numeric value corresponding to right triangle side ratios. A circle of radius 1 is defined by the points (cos(angle), sin(angle)) for angles ranging from 0 to 2 * pi radians.

The trigonometric functions are as follows:

· math.sin (angle in radians): Returns the sine of the specified angle. See Figure 11-1 for a plot of this function.

11-1

Figure 11-1

· math.cos (angle in radians): Returns the cosine of the specified angle. See Figure 11-2 for a plot of this function.

11-2

Figure 11-2

· math.tan (angle in radians): Returns the tangent of the specified angle. See Figure 11-3 for a plot of this function.

11-3

Figure 11-3

Inverse Trigonometric Functions

These functions return a numeric angle in radians that corresponds to a sine, cosine or tangent value. Because cosine and sine values range between -1 and 1, the arcsine and arccosine functions are defined only for arguments in this range.

The inverse trigonometric functions are as follows:

· math.asin (value from -1 to 1): Returns the angle in radians whose sine is specified. See Figure 11-4 for a plot of this function.

11-4

Figure 11-4

· math.acos (value from -1 to 1): Returns the angle in radians whose cosine is specified. See Figure 11-5 for a plot of this function.

11-5

Figure 11-5

· math.atan (value): Returns the angle in radians whose tangent is specified. See Figure 11-6 for a plot of this function.

11-6

Figure 11-6

· math.atan2 (numerator, denominator): Returns the angle of a bidimensional vector (numerator, denominator). The denominator may be zero.

This example runs through a range of numerators and denominators for the arctangent function:

for Y = -3, 3 do

for X = -3, 3 do

io.write(string.format("%5.2f ", math.atan2(Y, X) / math.pi))

end

io.write("\n")

end

The example prints the following lines:

-0.75 -0.69 -0.60 -0.50 -0.40 -0.31 -0.25

-0.81 -0.75 -0.65 -0.50 -0.35 -0.25 -0.19

-0.90 -0.85 -0.75 -0.50 -0.25 -0.15 -0.10

1.00 1.00 1.00 0.00 0.00 0.00 0.00

0.90 0.85 0.75 0.50 0.25 0.15 0.10

0.81 0.75 0.65 0.50 0.35 0.25 0.19

0.75 0.69 0.60 0.50 0.40 0.31 0.25

Hyperbolic Functions

These functions are to the hyperbola what the trigonometric functions are to the circle. The right half of the equilateral hyperbola is defined by the points (cosh(hyperbolic angle), sinh(hyperbolic angle)).

The hyperbolic functions are as follows:

· math.sinh (hyperbolic angle): Returns the hyperbolic sine of the specified value. See Figure 11-7 for a plot of this function.

11-7

Figure 11-7

· math.cosh (hyperbolic angle): Returns the hyperbolic cosine of the specified value. See Figure 11-8 for a plot of this function.

11-8

Figure 11-8

· math.tanh (hyperbolic angle): Returns the hyperbolic tangent of the specified value. See Figure 11-9 for a plot of this function.

11-9

Figure 11-9

Exponent Functions

These functions return the value of a number raised to another:

· math.exp (natural exponent) : Returns the value of e raised to the specified exponent. See Figure 11-10 for a plot of this function.

11-10

Figure 11-10

· math.pow (base, exponent): Returns the specified base raised to the specified exponent. The following example demonstrates this function:

local Base, Exp = 0, 10

local Pow = math.pow(Base, Exp)

io.write(Base, " ^ ", Exp, " = ", Pow == 0 and"nothing at all" or Pow, "\n")

-- With a friendly tip of the hat to Ian Anderson

The following line is printed by the example:

0 ^ 10 = nothing at all

· math.sqrt (number): Returns the square root of the specified number. See Figure 11-11 for a plot of this function.

11-11

Figure 11-11

Logarithm Functions

These functions are the inverse of the exponent functions:

· math.log (positive number): Returns the natural (base-e) logarithm of the specified number. The number e raised to the return value equals the specified argument. See Figure 11-12 for a plot of this function.

11-12

Figure 11-12

· math.log10 (number): Returns the base-10 logarithm of the specified number. The number 10 raised to the return value equals the specified argument. See Figure 11-13 for a plot of this function.

11-13

Figure 11-13

Adjustment Functions

These functions adjust the specified value by sign or value:

· math.abs (number): Returns the absolute value of the specified number; a non-numeric argument causes an error. See Figure 11-14 for a plot of this function.

11-14

Figure 11-14

· math.ceil (number) : Returns the smallest integer greater than or equal to the specified numeric argument. See Figure 11-15 for a plot of this function.

11-15

Figure 11-15

· math.floor (number) : Returns the greatest integer less than or equal to the specified numeric argument. See Figure 11-16 for a plot of this function.

11-16

Figure 11-16

Floating Point Representation

These functions are used in the representation of floating point numbers as the product of a mantissa and two raised to an integer exponent:

· math.frexp (number): Returns the mantissa and integer exponent of the specified number. The returned values are adjusted so that the absolute value of the mantissa is greater than or equal to 0.5 and less than 1.0, except where the specified number is 0, in which case both return values are 0.

· math.ldexp (mantissa, integer exponent): Returns the product of the specified mantissa and two raised to the specified exponent.

This example breaks the value pi into a mantissa and exponent and then recombines them:

local Mantissa, Exp = math.frexp(math.pi)

io.write("Pi: ", Mantissa, " * 2 ^ ", Exp,

math.ldexp(Mantissa, Exp), "\n")

The following line is printed by the example:

Pi: 0.78539816339745 * 2 ^ 2 = 3.1415926535898

Angle Conversion Functions

These helper functions convert angle values between degrees and radians:

· math.deg (angle in radians) : Returns the number of degrees in the specified number of radians.

For example, this converts some radian values to degrees:

io.write("Radians Degrees\n")

io.write("------- -------\n")

for Step = 0, 2, 0.25 do

local Degree = math.deg(math.pi * Step)

io.write(string.format("%4.2f pi %7.1f\n", Step, Degree))

end

The example prints the following table:

Radians Degrees

------- -------

0.00 pi 0.0

0.25 pi 45.0

0.50 pi 90.0

0.75 pi 135.0

1.00 pi 180.0

1.25 pi 225.0

1.50 pi 270.0

1.75 pi 315.0

2.00 pi 360.0

· math.rad (angle in degrees) : Returns the number of radians in the specified number of degrees. For example, this redefines math.sin to accept its angle argument in degrees rather than radians:

math._sin = math.sin

-- Redefine math.sin to accept angle in degrees

math.sin = function(degrees)

return math._sin(math.rad(degrees))

end

io.write(" x sin(x)\n")

io.write("--- ------\n")

for Deg = 0, 360, 90 do

io.write(string.format("%3d %6.2f\n", Deg, math.sin(Deg)))

end

The example prints the following table:

x sin(x)

--- ------

0 0.00

90 1.00

180 0.00

270 -1.00

360 -0.00

Pseudo-Random Number Functions

These functions are used to generate pseudo-random numbers:

· math.randomseed (number): Seeds the pseudo-random number generator. You can obtain identical sequences of pseudo-random numbers by calling this function with the same value.

· math.random (optional bound, optional bound): Returns a pseudo-random number in one of three ways. If no argument is specified, the return value is greater than or equal to 0 and less than 1. If one number is specified, the return value is an integer greater than or equal to 1 and less than or equal to the specified argument. If two numbers are specified, the return value is an integer greater than or equal to the first argument and less than or equal to the second.

This example shows how you can obtain identical sequences of pseudo-random numbers by seeding the generator with the same value:

local Seed = os.clock()

for J = 1, 2 do

math.randomseed(Seed)

math.random() -- On some systems that shall remain nameless, the

-- first call to math.random is easy enough to predict that it

-- destroys the illusion of randomness; throw it away.

for J = 1, 10 do

io.write(math.random(100, 200), " ")

end

io.write("\n")

end

The example prints identical sequences of numbers (the particular values depend on the seed value returned from os.clock):

123 165 107 127 136 125 199 132 194 118

123 165 107 127 136 125 199 132 194 118

Modulus Functions

These functions return the remainder of a division or the fractional part of an expression:

· math.modf (number) : Returns the integer and fractional portions of the specified number. Both of the return values carry the same sign as the argument.

· math.fmod (numerator, denominator): Returns the remainder of the numerator divided by the denominator. The return value is undefined for a denominator equal to 0.

This example shows various manipulations of rational numbers:

local function Rational(Num, Den)

local Whole, Frac = math.modf(Num / Den)

io.write(Num, " / ", Den, " = ", Whole, "+ ", Frac, "\n")

io.write(Num, " / ", Den, " = ", Whole, "+ ",

math.fmod(Num, Den), " / ", Den, "\n")

end

Rational(5, 3)

Rational(-5, 3)

Rational(5.2, math.pi)

The example prints the following lines:

5 / 3 = 1 + 0.66666666666667

5 / 3 = 1 + 2 / 3

-5 / 3 = -1 + -0.66666666666667

-5 / 3 = -1 + -2 / 3

5.2 / 3.1415926535898 = 1 + 0.65521140815571

5.2 / 3.1415926535898 = 1 + 2.0584073464102 / 3.1415926535898

Minimum and Maximum Functions

These functions return the minimum or maximum value in a list of values:

· math.max (one or more numbers) : Returns the greatest value in the specified list.

· math.min (one or more numbers) : Returns the smallest value in the specified list.

This example returns the minimum and maximum values from an array:

local function Range(... )

io.write("Count: ", select("#" . . .), ", Min: ", math.min(...),

", Max: ", math.max(...), "\n")

end

Range(3, 1, 72, 5.4, math.pi)

The following line is printed by the example:

Count: 5, Min: 1, Max: 72

Constants

These are not functions, but numbers that may be useful in certain calculations:

· math.huge: A number greater than or equal to any other number. This is positive infinity on systems that have a representation for infinity. The following example multiplies each number from NumListA with its counterpart in NumListB and prints the highest product obtained. Max is initialized to -math.huge (note the unary minus), which is guaranteed not to be greater than any of the actual maximum products.

NumListA = {2, 5, 3}

NumListB = {5, 3, 2}

local Max = -math.huge

for I, NumA in ipairs(NumListA) do

local NumB = NumListB[I]

Max = math.max(Max, NumA * NumB)

end

io.write("The maximum product was ", Max, ".\n")

The example prints the following:

The maximum product was 15.

· math.pi: The ratio of a circle's circumference to its diameter. For example, this computes a circle's area from its radius:

function CircleArea(Radius)

return math.pi * Radius ^ 2

end

Radius = 5

io.write("radius: ", Radius, " feet; area: ",

CircleArea(Radius), " square feet\n")

The example prints the following:

radius: 5 feet; area: 78.539816339745 square feet

Input/Output Library

The functions in the input/output (I/O) library enable your program to read from and write to files and the standard input/output streams.

By default, the io.read and io.write functions read from and write to standard input and standard output respectively. You can change this behavior with the io.input and io.output functions.

When working with multiple files, it is usually more convenient to obtain file handles and use their associated methods for reading, writing and performing other file operations.

Here are all the functions in the I/O library:

· io.close (optionalfile): Closes the default output file if no argument is specified; otherwise it closes the specified file. (See also the file handle close method.)

· io.flush(): Commits the default output buffer.

· io.input (optional file or name of file): Returns the default input file handle if called without an argument. If it is called with a handle to an open file, the file becomes the new default input file so that calls to io.read read from it. If the function is called with the name of a file, the file is opened in text mode and becomes the new default input file.

· io.lines (optional name of file): Returns an iterator for use in a generic for loop. If a filename is specified, the file is opened in text mode. When all lines have been read, the file is closed. If no argument is specified, the for loop reads lines from the default input file. In this case, the file is not closed after all lines have been read.

· io.open (name of file, optional open mode): Opens the specified file and returns a handle to it. (The returned file handle's methods are discussed later in this section.) If an error occurs, it returns nil followed by an error message. If the open mode is missing, the file is opened in text mode for reading. The following table lists the supported modes.

On platforms such as Windows, you can disable the control code processing done for text files by appending "b" to the open mode, which opens the file in binary mode.

11-2

· io.output (optional file or name of file): Returns the default output file handle if called without an argument. If it is called with a handle to an open file, the file becomes the new default output file so that calls to io.write write to it. If the function is called with the name of a file, the file is opened in "w" mode (that is, it is overwritten if it exists, or created if it doesn't) and becomes the new default output file.

· io.read (zero or more of "*a" or "*n" or "*1" or number): Returns the next line from the default input file if no argument is specified. The argument specifies what kind and how much data is to be read. Its meaning is summarized in the following table.

If data corresponding to the specified format cannot be read, nil is returned (but is not followed with an error message).

11-3

· io.tmpfile() : Opens a temporary file for reading and writing and returns a handle to it. When the program ends normally, this file will be deleted.

· io.type (file) : Returns "file", "closed file", or nil depending on the argument.

· io.write (zero or more strings or numbers): Writes each of its arguments to the default output file, and returns true if the write is successful. An error is issued if the default output file is closed or a value other than a string or number is written. In certain cases (for example, if the full disk is full), nil followed by an error message is returned.

· io.popen (shell command, optional mode): Launches the specified command and returns a handle. If the mode is "r" or not given, this handle can be used as a file handle to read the standard output of the command. If it is "w", the handle can be used to write to the command's standard input. This function is not available on all platforms.

This example demonstrates the execution of a command and the capturing of its output:

Hnd, ErrStr = io.popen("lua -h")

if Hnd then

for Str in Hnd:lines() do

io.write(Str, "\n")

end

Hnd:close()

else

io.write(ErrStr, "\n")

end

The example produces the following output:

usage: lua [options] [script [args]].

Available options are:

-e stat execute string 'stat'

-l name require library 'name'

-i enter interactive mode after executing 'script'

-v show version information

-- stop handling options

- execute stdin and stop handling options

The following functions are all the methods of file handles:

· file handle:close():Closes the file.

· file handle flush():Commits the file's output buffer.

· file handle lines():Like io.lines, this returns an iterator for use in a generic for loop using the specified file for input. Unlike io.lines, it does not close file associated file after the traversal.

· file handle:read(zero or more of "*a" or "*n" or "*1" or number) : Like io.read, this reads data from the specified file.

· file handle :seek( optional "set" or "cur" or "end", optional offset): Sets the file pointer in the specified file and returns the new position measured from the beginning of the file. Unlike most Lua index values, the offsets to this function are zero-based. The offset is measured from the beginning of the file if the first argument is "set"; from the current position in the file if it's "cur"; or from the end of the file if it's "end". The default argument values are "cur" and 0, so the current file position can be obtained by calling this function without arguments.

· file handle:setvbuf("no" or "full" or "line", optional size): Configures the buffering used by the specified file. The size argument specifies the size of the buffer in bytes. This function returns true on success, or nil followed by an error message on failure. The following table describes the buffering modes. (For more detail on this function, consult the documentation for the C function setvbuf.)

11-4

· file handle : write (zero or more strings or numbers) : Writes each of its arguments to the file. An error is issued if the file is closed or a value other than a string or number is written. In certain cases (for example, if the disk is full), this function returns nil followed by an error message.

This example demonstrates full buffering with a file so that only a full buffer or explicit flush commits the written data to the file, as well as file pointer positioning:

FileStr = "test.txt"

Hnd, ErrStr = io.open(FileStr, "w")

if Hnd then

Hnd:setvbuf("full", 1024)

for K = 1, 4 do

if K == 3 then

Pos = Hnd:seek("cur", 0)

Hnd:write("Special line\n")

end

Hnd:write(string.format("Line %02d\n", K))

end

Hnd:close()

Hnd, ErrStr = io.open(FileStr, "r")

if Hnd then

for Str in Hnd:lines() do

io.write(Str, "\n")

end

Hnd:seek("set", Pos)

Str = Hnd:read("*l")

io.write(Str, "\n")

Hnd:close()

else

io.write(ErrStr)

end

else

io.write(ErrStr)

end

The example prints the following lines:

Line 01

Line 02

Special line

Line 03

Line 04

Special line

Operating System Library

These functions provide access to the operating system's time, date, shell, and file management services.

CPU Timing

Identifying bottlenecks is sometimes as simple as monitoring the amount of time the central processing unit spends on various parts of a Lua program. In general, you should repeat the code you want to examine a sufficient number of times with a loop so that you have figures well above the noise level.

The operating system library includes the os.clock() timing function, which returns the approximate number of CPU time measured in seconds used by the current program. For example, this gives a bench-mark for adding elements to an array:

local Count = 1e7

local Tbl = { }

local Tm = os.clock()

for J = 1, Count do

table.insert(Tbl, J)

end

io.write("table.insert: ", os.clock() - Tm, " sec\n")

Tbl = {}

Tm = os.clock()

for J = 1, Count do

Tbl[#Tbl + 1] = J

end

io.write("Indexed insert: ", os.clock() - Tm, " sec\n")

The example prints the following lines:

table.insert: 5.328 sec

Indexed insert: 3.906 sec

Time and Date Functions

The functions in the time and date category are flexible enough to handle a wide spectrum of applications:

· os.date (optional format string, optional time value): Returns the current time or the specified time either as a string or as an associative table. A table is returned if the format string is "*t"; otherwise it uses the same format specification as strftime in the standard C library. To return the time in Universal Coordinated Time format, prepend the format specifier with “!”. If you don't specify a time value, the current time is used. If you do specify it, use the format returned by os.time.

· os.difftime (high time, low time): The difference is seconds between the two time arguments is returned. Use the format returned by os.time for the arguments.

· os.time (optional time table): Returns the specified time as a scalar value, often the number of seconds that have passed in a standard epoch. If you do not specify the time argument, the current time is used. If you do specify it, do so with a table that contains the year, month and day fields. You can include hour, min, sec and isdst in the table as well.

The following example calculates the number of seconds from a given moment:

TmStart = os.time{year = 2001, month = 1, day = 1, hour = 0}

TmNow = os.time()

io.write(os.difftime(TmNow, TmStart), " seconds have passed since ",

os. date("%d %B %Y", TmStart), "\n")

The following line is printed by the example:

180787215 seconds have passed since 01 January 2001

Filesystem Functions

In addition to the I/O functions, the standard C library provides paltry support for managing files. (A number of modules for Lua remedy this deficiency.)

The filesystem functions are as follows:

· os.remove (name of file): Removes the specified file. If successful, this returns true; otherwise it returns nil followed by an error message. On Unix-type systems, you can use this function to remove an empty directory.

· os.rename (old name of file, new name of file): Renames a file or directory.

· os.tmpname() : Returns a name that can you can use to create a temporary file or directory.

This example demonstrates how a temporary file is created, written to, closed, and then read from:

FileStr = os.tmpname()

Hnd = io.open(FileStr, "w")

if Hnd then

io.write("Opened temporary file ", FileStr, " for writing\n")

Hnd:write("Line 1\nLine 2\nLine 3\n")

Hnd:close()

for Str in io.lines(FileStr) do

io.write(Str, "\n")

end

os.remove(FileStr)

else

io.write("Error opening ", FileStr, " for writing\n")

end

The following lines are printed by the example:

Opened temporary file /tmp/lua_a05eSj for writing

Line 1

Line 2

Line 3

Other Operating System Functions

The remaining functions in the os table let your Lua script interact with the operating shell and its environment variables, and use locale settings to control language- and country-specific 'margin-top:1.5pt;margin-right:0cm;margin-bottom:1.5pt; margin-left:5.0pt;text-align:justify;text-indent:0cm;line-height:normal'>· os.execute (optional shell command string) : The specified command is passed to the shell for execution. The code returned from the shell is in turn returned by this function. If the command is missing, this function checks for the existence of the shell, returning a non-zero value if it is available and zero if it is not.

· os.exit( (optional integer code): Terminates the host program. If the exit code is missing, the library will provide the code that indicates success for the platform on which the host is running; this is usually zero.

· os.getenv (variable name string): Retrieves the value associated with the argument in the environment of the current process. If the name doesn't exist in the environment, nil is returned.

· os.setlocale (locale string, optional "all" or "collate" or "ctype" or "monetary" or "numeric" or "time"): Queries or sets locale information for the current program. To perform a query, specify nil as the first argument. To set the locale information, specify a locale string such as "pt_BR" or "en_US". The supported categories are "all", "collate", "ctype", "monetary", "numeric", and "time". If the category is missing, "all" is assumed.

Here is an example of os.execute, os.getenv, os.setlocale, and os.exit in action:

print("os.execute returned:", os.execute("ls -sh")) -- "ls" is a

-- directory listing program available on Unix-like systems.

print("HOME environment variable:", os.getenv("HOME"))

assert(os.setlocale("pt_BR", "numeric"))

print(math.pi) -- Prints with a comma!

os.exit()

print("This will never get printed!")

The example's output is as follows:

total 8.0K

4.0K calltrace.lua

4.0K test.lua

os.execute returned: 0

HOME environment variable: /home/wthrall

3,1415926535898

Debugging Library

The functions in the debug namespace table are not for normal use, but for writing error handlers, debuggers, profilers, and other things that need to do introspection (the inspection of running code). For a more detailed discussion of these functions, see Chapter 10.

Some of these functions take an optional thread argument. Those functions work on the given thread, or (by default) on the currently running thread. Even if the thread argument is omitted, you can specify other arguments by shifting their positions to the left. For example, you can use thedebug.getinfo function or stack level argument as either its first or second argument, depending on whether the optional thread argument was given. Arguments that are stack levels, local indexes, or upvalue indexes are positive integers, with 1 representing the top of the stack, the first local variable, or the first upvalue.

The following functions are in the debugging library:

· debug.debug() : Reads and interprets lines from standard input, and returns when it reads a line consisting of the word "cont". Local variables visible in the scope where debug.debug is called are only accessible using debug.getlocal, debug.setlocal, debug.getupvalue, and debug.setupvalue.

· debug.getfenv (function or thread or userdata) : Returns its argument's environment table.

· debug.gethook (optional thread) : Returns the current hook function, the current hook mask (a string containing zero or more of "c", "1", and "r"), and the current hook count.

· debug.getinfo (optional thread, function or stack level, optional flag string): If a function is given, this returns an associative table of information about that function; if a stack level is given, it returns an associative table of information about the function running at that stack level, or nil if the stack level is invalid. By default, the returned table includes the fields currentline, func, lastlinedefined, linedefined, name, namewhat, nups, short_src, source, and what. The letters in the flag string control which fields the table contains. For example, if the flag string contains an "L", then the table will include an activelines field. (For more details, see the table in Chapter 10.)

· debug.getlocal (optional thread, stack level, local index): Returns the name and value of the numerically specified local variable at the specified stack level. If the local index is higher than the amount of local variables at the specified stack level, nil is returned. If the stack level is higher than the amount of active stack levels, an error occurs.

· debug.getmetatable (value) : Returns its argument's metatable if one is present; otherwise it returns nil. This function bypasses its argument's __metatable metamethod if one is present.

· debug.getregistry() : Returns the registry table, a table that C code can use as a storage area.

· debug.getupvalue (function, upvalue index) : Returns the name and value of the numerically specified upvalue of the given function. If the upvalue index is higher than the function's amount of upvalues, nil is returned.

· debug.setfenv (function or thread or userdata, environment table): Gives an environment table to a function, thread, or userdata.

· debug.sethook (optional thread, hook function, hook mask string with "c" and/or "r" and/or " l", optional instruction count): Makes the given function the hook (a function that is called by Lua when certain events happen). The hook mask (a string) and the instruction count determine which events trigger the hook. If the hook mask contains "c", then on every function call (including tail calls), the hook will be called with the "call" argument. If the hook mask contains "r", then on every return (including tail returns) the hook will be called with the "return" or "tail-return"argument. If the hook mask contains "l", then on every source code line (except for lines where nothing happens) the hook will be called with the "line" and the line number arguments. If there is a nonzero instruction count, then every time that many bytecode instructions have been executed, the hook will be called with the "count" argument. If nil is given instead of a function, then any function that is currently set as the hook is removed.

· debug.setlocal (optional thread, stack level, local index, value): Assigns the given value to the numerically specified local variable at the specified stack level, and returns the local variable's name. If the local index is higher than the amount of local variables at the specified stack level, nil is returned; if the stack level is higher than the amount of active stack levels, an error occurs.

· debug.setmetatable (value, metatable): Gives a metatable to the given value, even if that value has a __metatable metamethod.

· debug.setupvalue (function, upvalue index, value) : Assigns the given value to the numerically specified upvalue of the given function, and returns the upvalue's name. If the upvalue index is higher than the function's amount of upvalues, nil is returned.

· debug.traceback (optional thread, optional message string, optional level argument): Returns a string containing the message string followed by a stack traceback. The level argument specifies which stack frame to start the traceback from, with 1 (the default) being the function that calleddebug.traceback, 2 being the function that called that function, 3 being the function that called that function, and so on.

This example sets a hook that is called on every line and every function call and return, which then prints information about the current line, call, or return:

function Hook(Event, LineNum)

-- Info for the currently running function (not counting this hook):

local Info = debug.getinfo(2)

if Event == "call" then

io.write("'", Info.name or "???", "' is being called; ")

if Info.nups == 0 then

io.write("it has no upvalues.\n")

else

io.write("its upvalues are:\n")

for I = 1, Info.nups do

local Name, Val = debug.getupvalue(Info.func, I)

io.write(" ", Name, "\t", tostring(Val), "\n")

end

end

elseif Event == "return" or Event == "tail return" then

io.write("'", Info.name or "???", "' is being returned from.\n")

elseif Event == "line" then

local LclCount = 0

-- An iterator that returns the names and values of the current

-- function's local variables:

local function Locals()

LclCount = LclCount + 1

return debug.getlocal(3, LclCount)

end

local Lcls = {}

for LclName, LclVal in Locals do

-- Ignore temporary locals used by the Lua virtual machine:

if string.sub(LclName, 1, 1) ~= "(" then

Lcls[#Lcls + 1] = LclName .. " (" .. tostring(LclVal) .. ")"

end

end

io.write("line ", LineNum, "; ")

if #Lcls > 0 then

-- This doesn't include upvalues:

io.write("in-scope local(s): ", table.concat(Lcls, ", "),

"\n")

else

io.write("no in-scope locals.\n")

end

end

end

local Fruit1, Fruit2 = "banana", "kiwi"

function FncB()

return Fruit1 .. "/" .. Fruit2 -- Line 48.

end

function FncA(Fruit)

local Fruits = FncB() -- Line 52.

return Fruit .. "/" .. Fruits -- Line 53.

end

debug.sethook(Hook, "crl")

print("### " .. FncA("apple")) -- Line 57.

debug.sethook(nil) -- Line 58.

The example gives the following output:

'sethook' is being returned from.

line 57; in-scope local(s): Fruit1 (banana), Fruit2 (kiwi)

'FncA' is being called; it has no upvalues.

line 52; in-scope local(s): Fruit (apple)

'FncB' is being called; its upvalues are:

Fruit1 banana

Fruit2 kiwi

line 48; no in-scope locals.

'FncB' is being returned from.

line 53; in-scope local(s): Fruit (apple), Fruits (banana/kiwi)

'FncA' is being returned from.

'print' is being called; it has no upvalues.

'???' is being called; it has no upvalues.

'???' is being returned from.

### apple/banana/kiwi

'print' is being returned from.

line 58; in-scope local(s): Fruit1 (banana), Fruit2 (kiwi)

'sethook' is being called; it has no upvalues.

Summary

In this chapter, you've reviewed some of the library functions from earlier in the book, and learned about others. Prior to this chapter, you learned about the Lua language. From this point on, you'll be exploring how Lua can serve as an integral component in real-world applications. The next chapter starts this off with a small survey of open-source libraries that work well with Lua.