Capricorn 76
A Lua tutorial looking at the major parts of the Lua programming language as used by the ScriptENGINE.


---------------------------
---------------------------
-- Tutorial: Hello world
-- Your first script
--
-- We suggest looking at the Lua reference manual bundled with the
-- ScriptENGINE Help to get a better understanding of the language
--
-- Copyright (c) 2006-2008 Capricorn 76 Pty. Ltd.
---------------------------
---------------------------

--[[
    This is a lua block comment that can span multiple lines and ignored by ScriptENGINE.
    A line comment starts with --
]]

---------------------------
-- Global and local variables
--
-- Lua is a dynamically typed language.
-- That means that variables do not have types; only values do.
-- There are no type definitions in the language.
-- All values carry their own type.
--
-- There are eight basic types in Lua: nil, boolean, number, string, function, userdata, thread, and table.
-- Nil is the type of the value nil, whose main property is to be different from any other value; usually it represents the absence of a useful value.
-- Boolean is the type of the values false and true. In Lua, both nil and false make a condition false; any other value makes it true.
-- Number represents real (double-precision floating-point) numbers. (It is easy to build Lua interpreters that use other internal representations for numbers, such as single-precision float or long integers.)
-- String represents arrays of characters. Lua is 8-bit clean: Strings may contain any 8-bit character, including embedded zeros ('\0'). Strings are defined in single or double quotes.
-- ScriptENGINE has extended Lua to also support wide-strings (Unicode). Wide strings may contain any 16-bit character, including embedded zeros. Wide strings are defined using the L"This is a wide sting" syntax.
-- The Lua library function type(v) returns a string describing the type of a given value
--
-- Variables are places that store values. There are three kinds of variables in Lua: global variables, local variables, and table fields.
---------------------------
a = 5;          -- This is a global variable, accessible from all loaded scripts, and assigned a number
local b = 'Trevor';     -- This is a local variable, accessible only in this script, and only by functions declared below this line.
local c;            -- Variables are assigned nil if no other value is used.

---------------------------
-- Garbage collection
--
-- Lua automatically manages garbage collection,
-- but you can manually control it too.
---------------------------
local function garbageCollectFunction()
    collectgarbage(0);  -- Force garbage collector to run immediately
end

---------------------------
-- Global functions
--
-- This is a global function, accessible from all loaded scripts including ScriptENGINE.
-- Your ScriptENGINE callbacks must be globally defined functions...
---------------------------
function globalFunction(param1, param2)
    print('Inside the global function', param1);    -- Calling a Lua library function.
end

---------------------------
-- Local functions
--
-- This is a local function, accessible only in this script, and only by functions declared below this line.
---------------------------
local function localFunction(param1, param2, param3)
    print('Inside the local function', param1, param2, param3);
    globalFunction(param1, param2);         -- Calling one Lua function from another

    local f = globalFunction;               -- Functions can also be stored in variables.
    f(param2, param3);                  -- Then you can call the functions using the variable (it's like a function pointer)
end

---------------------------
-- Functions with return values
--
-- Your functions can return 1 or more values to their calling function by separating the return values with a comma
-- By default, all functions will return nil is no other values are returned.
---------------------------
local function returningFunction()
    return 'value1', 1, 4.5;

    -- To grab all the return values, your calling function would look something like:
    -- local v1, v2, v3 = returningFunction()
end


---------------------------
-- Lua control structures: if-statements, for-loops, while-loops
---------------------------
local function loopFunctions(totalLoops)
    -- Check the input parameter. Make sure it was defined (if not, we'll assign a default value)
    if (not(totalLoops)) then
        totalLoops = 10;
    end

    if (totalLoops > 100) then  -- Check that input param is not too large
        totalLoops = 100;
    end

    local i;    -- The variable is assigned 'nil' by default
    for i = 1, totalLoops do
        print('loopFunctions for-statement:', i);
    end

    i = totalLoops;
    while i > 0 do
        print('loopFunctions while-statement:', i);
        i = i - 1;
    end
end

---------------------------
-- Lua tables
--
-- Lua tables are one of the most important parts of the language. They are very powerful.
-- Think of lua tables like arrays on steroids.
-- Tables can hold any lua data type, including other tables.
---------------------------
local function tableFunction()
    local exampleEmptyTable = {};
    local tableIndexedWithNumbers = {'value1', 'value2', 'value3'};
    local tableIndexedWithStrings = {val1='value1', val2='value2', val3='value3'};

    -- Accesing table values in a range of styles
    -- Notice that you can use the dot-notation to access names table values. this is the basis to creating objects in Lua
    print('Table values:', tableIndexedWithNumbers[1], tableIndexedWithStrings['val1'], tableIndexedWithStrings.val2);

    -- Iterating tables
    local i;
    for i = 1, table.getn(tableIndexedWithNumbers) do
        -- Demonstrating string-concatenation with the double-dot notation
        print('tableIndexedWithNumbers ' .. tostring(i) .. ':', tableIndexedWithNumbers[i]);
    end

    -- Inserting an item into the table
    table.insert(tableIndexedWithNumbers, 2, 'value4');

    -- Iterating tables using the Lua pairs() function
    local v;
    for i, v in pairs(tableIndexedWithNumbers) do
        print('tableIndexedWithNumbers B ' .. tostring(i) .. ':', v);
    end

    for i,v in pairs(tableIndexedWithStrings) do
        print('tableIndexedWithStrings ' .. tostring(i) .. ':', v);
    end

end

---------------------------
-- Lua coroutines
--
-- These are different to ScriptENGINE threads and not actually running in separate thread.
-- Instead they provide different execution paths, that you can switch between.
---------------------------
function foo (a)
    print("foo", a);
    return coroutine.yield('Yielding from foo');        -- This will stop execution of the current coroutine, and return.
end

local function coroutineFunction(a)

      print("coroutineFunction:", a);
      local r = foo('Called from coroutineFunction');   -- The coroutine will yield exection at this point

      print("foo returned to coroutineFunction:", r);               -- When resumed, the coroutine will restart here
      local r = coroutine.yield('Yielding from coroutineFunction');     -- Yielding again, the coroutine will stop execution

      print("coroutineFunction resumed:", r);       -- When resumed, the coroutine will restart here

      return "coroutineFunction complete";          -- Coroutine complete, can no longer be resumed
end

local function runCoroutineFunction()
    -- Create a coroutine, using a pre-defined function.
    -- The coroutine.wrap function is an alternate method for creating a coroutine
    local co = coroutine.create(coroutineFunction);
    local yieldResult, returnValue;

    yieldResult, returnValue = coroutine.resume(co, 'Starting coroutineFunction');      -- Start coroutine
    print("main", yieldResult, returnValue);                                -- After the 1st yield, the coroutine will return to here

    yieldResult, returnValue = coroutine.resume(co, "Resuming coroutineFunction 1");    -- Resume coroutine
    print("main", yieldResult, returnValue);                                -- After the 2st yield, the coroutine will return to here

    yieldResult, returnValue = coroutine.resume(co, "Resuming coroutineFunction 2");    -- Resume coroutine
    print("main", yieldResult, returnValue);                                -- After the 3st yield (the return call), the coroutine will return to here

    yieldResult, returnValue = coroutine.resume(co, "Resuming coroutineFunction 3");    -- Can no longer resume the coroutine. It's dead
    print("main", yieldResult, returnValue);
end


---------------------------
-- Calling ScriptENGINE interfaces
--
-- One of the first things you'll need to learn is how to call an ScriptENGINE function.
-- Here, we are using the IApp interface to open the console window.
-- If you don't explicitly pass a function parameter to ScriptENGINE, it will default to [nil/0/false/''] depending upon the input data type.
---------------------------
local function startConsole()
    IApp:showConsoleWindow();   -- This will open the console window, so our print statements are displayed
end

---------------------------
-- Creating Lua classes.
-- Lua can be extended to support classes and objects.
-- ScriptENGINE supplies functions in the IMisc interface to define classes & create class instances.
-- The ScriptENGINE class implementation supports constructors, destructors, and polymorphism (ie. virtual functions).
---------------------------
local function luaClassExample()
    ---------------------------
    -- Create a class definition
    -- Must call this function before any other class functions are defined
    ---------------------------
    myclass = IMisc:classDefine(nil);   -- The class function can take a base class as a parameter. It will inherit all the base class functions.

    ---------------------------
    -- Define class constructor using name __construct().
    -- Supply your input parameters, these will be passed into the constructor from the IMisc:classCreateInstance() function
    ---------------------------
    function myclass:__construct(param1)
        print('myclass:__construct:', param1);
        self.value1 = param1;
    end

    ---------------------------
    -- Define class destructor using name __destruct().
    -- Supply your input parameters, these will be passed into the constructor from the IMisc:classCreateInstance() function
    -- The destructor is automatically called by the Lua garbage collector when all references to a class instances have been set to nil.
    ---------------------------
    function myclass:__destruct()
        print('myclass:__destruct');
    end

    ---------------------------
    -- Define some class functions
    ---------------------------
    function myclass:getParam()
        return self.value1;
    end

    function myclass:setParam(newValue)
        self.value1 = newValue;
    end

    function myclass:func(param1)
        print('myclass:func:', param1);
    end


    ---------------------------
    -- Create a new instance of the class, and call its functions
    ---------------------------
    local obj = IMisc:classCreateInstance(myclass, 99); -- Construct a new object. Supply input parameters.

    -- Call a class-function on the object instance
    obj:func('abcd');

    -- Access the object's data using a class-function
    print('myclass:getParam:', obj:getParam());

    -- Set the object reference to nil. All references to this object are now nil,
    -- and Lua's garbage collector will collect this object (calling its destructor)
    -- on the next garbage collecting cycle.
    obj = nil;

    -- Force Lua to run its gabage collector immediately, so we can see the destructor being called
    collectgarbage();
end


---------------------------
-- Debugging ScriptENGINE programs
--
-- The IDebug interface provides some utility functions to debug your programs.
-- You can either access the debug data and present it in your own style (ie. via a GUI)
-- or use the output functions that write the debug info to the console.
---------------------------
local function showCallstack()
    local ci, li = 1, 1;
    local call_info, local_name, local_value;
    repeat
        call_info = debug.getinfo(ci);

        if (call_info) then
            -- Print the callstack information
            print('====' .. (ci - 1) .. '====');
            table.foreach(call_info, print);

            -- Print the local variables
            li = 1;
            print('---- Locals ----');
            repeat
                local_name, local_value = debug.getlocal(ci, li);

                if (local_name) then
                    print(local_name, local_value);
                    li = li + 1;
                end

            until (local_name == nil);

            ci = ci + 1;
        end

    until (call_info == nil);
end



---------------------------
---------------------------
-- SCRIPT - ENTRY - POINT
-- Code not explicitly placed in a function will be executed as soon as the script is loaded.
-- It's the entry-point to your ScriptENGINE programs.
---------------------------
---------------------------

-- Calling ScriptENGINE functions example
startConsole();

-- Local function example
print('\n### Calling local and global functions. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
localFunction("a", 5, true);

-- Functions with return values example
print('\n### Using functions with return values. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
local v1, v2, v3 = returningFunction();
print('returningFunction:', v1, v2, v3);

-- Loop functions example
print('\n### For and while loops. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
loopFunctions(5);

-- Table example
print('\n### Table example. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
tableFunction();

-- Coroutine example
print('\n### Coroutines example. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
runCoroutineFunction();

-- Lua class example
print('\n### Lua class example. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
luaClassExample();

-- Display debug info
print('\n### Viewing debug information. Press any key to start...');
IKey:getch();   -- Wait for the user to press a key
showCallstack();

print('\n### Tests complete. Press any key...');
IKey:getch();   -- Wait for the user to press a key

Copyright © 2006-23 Sep 2009 Capricorn 76 Pty. Ltd. (created on Wed Sep 23 16:49:11 2009)