#!Lua50.exe
-- But works better with Lua 5.1, which I used to generate the HTML file...
--[[
A non-exhaustive look at table and array capabilities of Lua.
Made for educational purpose, and to allow comparison of syntax/features
of JavaScript and PHP in the same domain.
by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr
File/Project history:
1.00 -- 2007/04/17 (PL) -- Creation.
]]
--[[ Copyright notice: See the PhiLhoSoftLicence.txt file for details.
This file is distributed under the zlib/libpng license.
Copyright (c) 2007 Philippe Lhoste / PhiLhoSoft
]]
eol = '\n'
function DoTest()
function StupidFunction(p) return p end
testPart = 3
---- Arrays with numerical index
-- Indexes in Lua start at 1
-- Simple definition and putting values in it
local simpleArray = {}
simpleArray[1] = "GET"
simpleArray[2] = "POST"
simpleArray[3] = "PUT"
simpleArray[4] = "DELETE"
-- Pre-definition and initialization
local predefinedArray = { nil, nil, nil, nil } -- Clumsy way, no other...
table.insert(predefinedArray, "HTTP")
table.insert(predefinedArray, "FTP")
table.insert(predefinedArray, "NNTP")
table.insert(predefinedArray, "IRC")
-- We can skip entries. But some functions like table.sort doesn't like that...
-- And table.getn will count only up to the first empty entry
-- Slots 5 and 6 are empty
table.insert(predefinedArray, table.getn(predefinedArray) + 3, "FTPS")
-- Definition and initialization at once
local initializedArray = { "html", "head", "body", } -- Trailing comma is OK
-- Generating an array
local generatedArray1 = Split("a:b:i:p:u:random:garbage", ":", 5)
local generatedArray2 = Split("hr , br ; p ,span, div", '%s*[;,]%s*')
-- Assigning an array
local gArray = Split("aa ! bb ! cc ! dd ! ee", " ! ")
local aArray = gArray -- aArray gets a reference, the arrays are the same
gArray[4] = "gg"
aArray[5] = "zz"
-- Array as function parameter and return value
function ProcessArray(a)
a[1] = "ff0" -- Change original array
local la = { "1", "2", "3", a } -- Get a reference
a[2] = "ff1" -- Change original array and la
return la
end
local fcArray = { "F1", "F2", "F3" }
local frArray = ProcessArray(fcArray)
-- Putting arrays in an array, and other objects too
local subArray1 = { "com", "org", "net", "int" }
local subArray2 = { "edu", "mil", "gov" }
local multiDimensionalArray =
{
22 / 7, -- You can compute values, of course
"TLDs",
subArray1,
subArray2,
StupidFunction,
42,
}
local result = ""
result = result .. FormatArrayMessage("simple", simpleArray)
result = result .. FormatArrayMessage("predefined", predefinedArray)
result = result .. FormatArrayMessage("initialized", initializedArray)
result = result .. FormatArrayMessage("generated 1", generatedArray1)
result = result .. FormatArrayMessage("generated 2", generatedArray2)
result = result .. FormatArrayMessage("original", gArray)
result = result .. FormatArrayMessage("assigned", aArray)
result = result .. FormatArrayMessage("function call", fcArray)
result = result .. FormatArrayMessage("function return", frArray)
result = result .. FormatArrayMessage("multi-dimensional", multiDimensionalArray)
AddResult(result, "Array Init")
-- Getting values:
result = ""
result = result .. simpleArray[3] .. eol
result = result .. predefinedArray[3] .. eol
result = result .. initializedArray[1 + 2] .. eol
result = result .. multiDimensionalArray[3][2] .. eol
result = result .. "[2] is: " .. predefinedArray[2] .. eol
result = result .. "[100] is: " .. (predefinedArray[100] or 'nil') .. eol
AddResult(result, "Array Access")
-- Some array operations
local r
result = ""
--~ predefinedArray[8] = "POP3"
-- Using table.insert updates the table size despite the holes, what [] doesn't do
-- As such, table.sort successfully takes out the empty slots with below function
table.insert(predefinedArray, 8, "POP3")
table.insert(predefinedArray, "Z1")
table.insert(predefinedArray, "Z2")
result = result .. FormatArrayMessage("array", predefinedArray) ..
-- getn is deprecated in Lua 5.1 but still works... Should use #predefinedArray in this version.
FormatMessage("getn", table.getn(predefinedArray)) ..
FormatMessage("type=='table'", tostring(type(predefinedArray) == 'table')) .. eol
-- Mutator methods
table.sort(predefinedArray,
-- I provide a sort function because default doesn't like empty elements...
function (a, b)
if a == nil then return false
elseif b == nil then return true
else return a < b
end
end
)
result = result .. FormatArrayMessage("sort", predefinedArray)
table.remove(predefinedArray) -- Remove last element
result = result .. FormatArrayMessage("remove (pop)", predefinedArray, "")
Reverse(predefinedArray)
result = result .. FormatArrayMessage("(reverse)", predefinedArray)
table.remove(predefinedArray, 1) -- Remove first element
result = result .. FormatArrayMessage("remove 1 (shift)", predefinedArray, "")
predefinedArray[1] = "HTTPS"
result = result .. FormatArrayMessage("(replace) 1", predefinedArray)
r = Splice(predefinedArray, 2, 2, "messaging") -- Remove 2, insert 1 in place
result = result .. FormatArrayMessage("Splice 2 2 s", predefinedArray, " '" .. (DumpObject(r)) .. "' removed")
r = Splice(predefinedArray, 4, 1) -- Remove 1
result = result .. FormatArrayMessage("Splice 4 1", predefinedArray, " '" .. (DumpObject(r)) .. "' removed")
r = Splice(predefinedArray, 2, 0, "POP3", "IMAP4") -- Insert 2
result = result .. FormatArrayMessage("Splice 2 0 s s", predefinedArray, " '" .. (DumpObject(r)) .. "' removed")
-- Add at end, no simple way to do it one op
table.insert(predefinedArray, "Y")
table.insert(predefinedArray, "Z")
result = result .. FormatArrayMessage("insert Y Z (push)", predefinedArray)
-- Add at beginning, idem
table.insert(predefinedArray, 1, "B")
table.insert(predefinedArray, 1, "A")
result = result .. FormatArrayMessage("insert B A (unshift)", predefinedArray)
-- Accessor methods
r = Slice(predefinedArray, 3, 7) -- Take a slice, from position 3 up to (not including) position 7
result = result .. FormatArrayMessage("slice 3 7", r)
r = Slice(predefinedArray, 7) -- Take a slice, from position 7 up to end
result = result .. FormatArrayMessage("slice 7", r)
predefinedArray = Slice(predefinedArray, 3, -2) -- Remove two elements at both ends
result = result .. FormatArrayMessage("slice 3 -2", predefinedArray)
predefinedArray = Concat(predefinedArray, simpleArray, initializedArray)
result = result .. FormatArrayMessage("(concat/merge) a1 a2 a3", predefinedArray)
result = result .. "No native search in values, just iterate on array to search" .. eol
result = result .. FormatMessage("concat ' | '", table.concat(predefinedArray, ' | '))
AddResult(result, "Array Operations")
function PrintElement(key, value)
result = result .. "[" .. key .. "] " .. value .. ", "
end
result = PadMessage"for ipairs"
for i in ipairs(predefinedArray) do
PrintElement(i, predefinedArray[i])
end
result = result .. eol
result = result .. PadMessage"for pairs"
for i in pairs(predefinedArray) do
PrintElement(i, predefinedArray[i])
end
result = result .. eol
AddResult(result, "Array Iteration")
---- Associative arrays
testPart = testPart + 2
-- Manual creation
local tableSimple = {}
tableSimple["one"] = "1"
tableSimple["two"] = "2_2"
tableSimple["three"] = "3_3_3"
tableSimple["#@!\\%"] = "garbage"
-- Table literal
local tableLiteral =
{
["one"] = "un_uno_ichi",
["two"] = "deux_dos_ni",
["three"] = "trois_tres_san",
["and... more"] = "...",
}
-- If the key name is a valid identifier, the quotes can be omitted
local tableId =
{
one = "1", un = 1, uno = true, ichi = ""
}
-- The values can be any object
local tableObj =
{
["1, 3.14"] = "1" .. StupidFunction(", " .. 22 / 7),
_ = tableId,
[0] = StupidFunction,
__ = { 4, 2 },
[3.14159] = aArray
}
local tableNested =
{
deep =
{
into =
{
table = '!'
}
}
}
local od = ""
result = ""
result = result .. FormatArrayMessage("simple", tableSimple)
result = result .. FormatArrayMessage("literal", tableLiteral)
result = result .. FormatArrayMessage("id", tableId)
result = result .. FormatArrayMessage("obj", tableObj)
result = result .. FormatArrayMessage("nested", tableNested)
AddResult(result, "Associative Array Init")
-- Getting values:
result = ""
result = result .. tableSimple["two"] .. eol
result = result .. tableSimple.three .. eol
result = result .. tableSimple["#@!\\%"] .. eol -- No shortcut here
result = result .. (tableSimple[1] or 'nil') .. eol -- Doesn't exist
result = result .. (tableSimple.unknown or 'nil') .. eol -- Doesn't exist
result = result .. tostring(tableObj._.uno) .. eol
result = result .. tableNested.deep.into.table .. eol
AddResult(result, "Associative Array Access")
-- Some array operations
result = ""
tableLiteral.newOne = "New entry"
tableLiteral[1] = 1
tableLiteral["+-/*"] = "ops"
result = result .. FormatArrayMessage("(add)", tableLiteral)
tableLiteral.two = nil
tableLiteral[1] = nil
tableLiteral["and... more"] = nil
result = result .. FormatArrayMessage("(delete)", tableLiteral)
AddResult(result, "Associative Array Operations")
-- Iteration is shown in the DumpObject function...
end -- function DoTest()
-- Emulation of functions found in other languages
-- Split the array str along the pattern delim, up to maxNb (default as no limits) items.
-- delim is part of a regular expression, you might need to escape some chars.
function Split(str, delim, maxNb)
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
if maxNb == nil or maxNb < 1 then
maxNb = 0 -- No limit
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gfind(str, pat) do
nb = nb + 1
result[nb] = part
lastPos = pos
if nb == maxNb then break end
end
-- Handle the last field
if nb ~= maxNb then
result[nb + 1] = string.sub(str, lastPos)
end
return result
end
function Reverse(t)
local l = table.getn(t) -- table length
local j = l
for i = 1, l / 2 do
t[i], t[j] = t[j], t[i]
j = j - 1
end
end
-- Emulate the splice function of JS (or array_splice of PHP)
-- I keep the imperfect parameter names from the Mozilla doc.
-- http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice
-- I use 1-based indices, of course.
function Splice(t, index, howMany, ...)
local removed = {}
local tableSize = table.getn(t) -- Table size
-- Lua 5.0 handling of vararg...
local argNb = table.getn(arg) -- Number of elements to insert
-- Check parameter validity
if index < 1 then index = 1 end
if howMany < 0 then howMany = 0 end
if index > tableSize then
index = tableSize + 1 -- At end
howMany = 0 -- Nothing to delete
end
if index + howMany - 1 > tableSize then
howMany = tableSize - index + 1 -- Adjust to number of elements at index
end
local argIdx = 1 -- Index in arg
-- Replace min(howMany, argNb) entries
for pos = index, index + math.min(howMany, argNb) - 1 do
-- Copy removed entry
table.insert(removed, t[pos])
-- Overwrite entry
t[pos] = arg[argIdx]
argIdx = argIdx + 1
end
argIdx = argIdx - 1
-- If howMany > argNb, remove extra entries
for i = 1, howMany - argNb do
table.insert(removed, table.remove(t, index + argIdx))
end
-- If howMany < argNb, insert remaining new entries
for i = argNb - howMany, 1, -1 do
table.insert(t, index + howMany, arg[argIdx + i])
end
return removed
end
-- Emulate the slice function of JS (or array_slice of PHP)
-- http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice
-- I use 1-based indices, of course.
function Slice(t, startPos, endPos)
local tableSize = table.getn(t) -- Table size
if endPos == nil then
-- Only one parameter: extract to end of table
endPos = tableSize + 1
end
if startPos < 0 then
startPos = tableSize + 1 + startPos
-- -1 -> last element
-- -2 -> last two elements, etc.
end
if endPos < 0 then
endPos = tableSize + 1 + endPos
end
local result = {}
for i = startPos, endPos - 1 do
result[i - startPos + 1] = t[i]
end
return result
end
-- Emulate the concat function of JS (or (more or less) array_merge of PHP)
-- http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat
-- Different of Lua's table.concat!
function Concat(...)
local argNb = table.getn(arg) -- Number of elements
local result = {}
for i = 1, argNb do
local a = arg[i]
if type(a) == 'table' then
for it = 1, table.getn(a) do
table.insert(result, a[it])
end
else
table.insert(result, arg[i])
end
end
return result
end
-- Helper functions
function DumpObject(obj)
local result = ""
local len = 0
if type(obj) ~= 'table' then
return tostring(obj), 0
end
-- First, walk the "array" part, with successive numerical indexes
-- As with the PHP and JS scripts, just put the values, indexes are implicit
for idx, value in ipairs(obj) do
if type(value) == 'table' then break end -- Stop there
result = result .. tostring(value) .. ","
len = len + 1
end
local arraySize = len
-- Walk the whole table
for key, value in pairs(obj) do
if type(key) == 'number' and key == math.floor(key) and key <= arraySize then
-- Pure integer in ipairs range, already processed
-- A continue keyword would be nice here...
else
-- Find elegant (legal) key notation
if type(key) == 'number' then
result = result .. "[" .. key .. "]"
elseif string.find(key, "^[%w_]+$") ~= nil then
-- Pure word, should exclude keywords...
result = result .. key
else
-- Simplified, I don't use too exotic keys here...
result = result .. "['" .. tostring(key) .. "']"
end
if type(value) == 'string' then
value = "'" .. value .. "'"
trail = ' '
elseif type(value) == 'table' then
value = "\n{ " .. (DumpObject(value)) .. " }"
trail = '\n'
else
value = tostring(value)
trail = ' '
end
result = result .. " = " .. value .. "," .. trail
len = len + 1
end
end
-- Remove trailing separator
return string.gsub(result, ",%s*$", ""), len
end
function PadMessage(message)
return message .. ':' .. string.rep(' ', 25 - string.len(message))
end
-- Data formating with message
function FormatMessage(message, data, after)
return PadMessage(message) .. data .. (after or '') .. eol
end
-- Array formating with message
function FormatArrayMessage(message, array, after)
local od, len = DumpObject(array)
return PadMessage(message) .. od .. " (" .. len .. ")" .. (after or '') .. eol
end
function AddResult(result, message)
table.insert(htmlPage, testPart, "<h3>" .. message .. "</h3>\n<pre>" .. result .. "</pre>")
testPart = testPart + 1
end
----- The script itself...
-- Template of HTML page
htmlPage =
{
-- 1
[[
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Testing Lua array (table) operations</title>
<!-- For dynamically generated pages -->
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Content-Language" content="en-EN" />
<meta name="Author" content="Philippe Lhoste" />
<meta name="Copyright" content="Copyright (c) 2006 Philippe Lhoste" />
<meta name="Generator" content="My typing hands with SciTE" />
<meta http-equiv="Keywords" content="test,Lua,language" />
<meta http-equiv="Description" content="A test page for Lua experiments" />
<style type="text/css">
body { background-color: #F0F8F0; }
h1
{
background-color: #BFC;
color: #008;
text-align: center;
padding: 20px;
}
h2
{
background-color: #CFD;
color: #048;
padding: 10px;
}
h3
{
background-color: #DFE;
color: #088;
padding: 5px;
}
pre
{
background-color: #FAFAFF;
color: #228;
}
</style>
</head>
]],
-- 2
[[
<body>
<h1>Testing Lua array/table operations</h1>
<h2>Array (table with numerical index)</h2>
]],
-- 3
[[ ]],
-- 4
[[
<h2>Associative array</h2>
]],
-- 5
[[ ]],
-- 6
[[
<hr />
<a href="TestLuaArray.lua.html">View source of script generating this page</a> /
<a href="TestLuaArray.lua">Get source of script</a>
</body>
</html>
]]
}
DoTest()
outputFileName = "TestLuaArray.html"
fh = io.open(outputFileName, "w")
fh:write(table.concat(htmlPage))
fh:close()