Comparison with C and C++
If you are a C or C++ programmer, you will notice many similarities between Lua and C or C++. However, Lua is not C or C++ and, in fact, there are many differences. You can learn more about these issues by reading Lua documentation such as the book Programming in Lua ("PIL"), which is available worldwide through large booksellers, or the Lua Reference Manual available from http://www.Lua.org (see Going Further with Lua).
Below are described some of the more important topics that are useful to know before beginning to write scripts. The list of topics discussed below is not complete, nor is it intended to substitute for a reading of Programming in Lua or other Lua documentation. These are just some highlights of the language that will be especially familiar to someone who has programmed in C, C++, or Pascal.
The basic execution unit of Lua is called a "chunk", or more commonly one might call it a module. Lua considers a chunk to be all of the source code contained in a single file. When Mira MX Script inserts another chunk into a script using the Include method, the chunk is compiled and executed. This adds a new dimension to the well known C language #include directive. You can see the benefit of this auto-execution in the example An Improved Filter Kernel Script, in which the Included chunk is pre-executed to compute filter kernels. Because of this architecture, it is useful to place classes and functions, as well as definitions, into separate chunks that you Include into the main script—or into other chunks themselves.
A comment is a line or block of text that is ignored by the Lua compiler. Using a comment allows you to annotate your Lua source text without affecting the executable code that Lua creates. Lua provides two types of comments:
An inline comment uses two dashes, ..., to signify an inline comment in the same way that C++ uses the // token. Everything from the -- to the end of line is ignored by the compiler. By default, the Mira Script Editor colors in-line comments green, like you see in the Microsoft Visual C++ source editor. -- this is a comment
A block comment is made using --[[ to begin the comment and --]] to end the comment. The block comment can extend over any number of lines. This works like the C language /* */ pair, except that the Lua --[[ --]] pair may contain anything whereas the /* */ cannot contain other /* or */ tokens.
Lua provides an assortment of C language-like operators such as + - * / ^ < > ==, ~=, >=, and <=. Operators follow the typical rules of precedence when used in expressions; for example, a/b + c/d is parsed as (a/b) + (c/d).
Also included are logical operators and, or, and not, which are equivalent to the C language &&, || and ~. There are two major differences between C operators and Lua operators: ~= and ^. The "not equals" operator is ~= in Lua but != in C. In Lua, ^ is the exponentiation operator but in C, ^ is the XOR operator.
Lua does not support pre-fix and post-fix operators ++ and--, or operators of the form += and &=.
Lua supports 8 data types, including numbers, strings, table, functions, and pointers. See Lua documentation for a detailed description.
Numbers are always double precision real values, like the double type in C and C++. Some Mira functions like Printf can use integer formats like %d, %x, and%p to treat numbers as integer or pointer types.
Strings have their usual meaning but you cannot access characters directly by treating the string as an array of characters. However, the string package included with Lua provides a rich collection of string manipulation and pattern matching functions.
Tables provide data structuring. A table is a collection that can be easily used for various kinds of data structuring applications. See the description of Tables, below.
Functions have their usual meaning. A function may return 0 or more values and return 0 or more arguments. The function is terminated by an end statement. Here are some examples:
function hoo( ... )
To invoke these functions, use them in an expression, as in these examples:
t = hoo( 4, 5, 6, 7, 8 )
Printme( "Hello world!" )
The 3rd example uses a function that takes a variable number of arguments and returns them bundled into a table. This uses Lua's hidden "arg" parameter.
Functions in Lua are first class values, meaning that a function name can be passed as an argument of another function.
Scope refers to the extent to which a value or function may be seen by other "parts" of the program. Values have two levels of scope: local and global. All values have global scope unless they are explicitly declared local using the local statement. A local variable its scope limited to the block where it is defined.
A block is defined as the body of a control structure (loop or if-elseif), a function, or a chunk. But declaring a variable inside a block does not make it local; a variable is global unless you explicitly declare it local, and then its scope is limited to the block where it was declared. This architecture is known as "Lexical Scope". By explicitly declaring the variable local, you are stating that you do not want it seen outside the block. For example:
hides the variable a from outside the function foo, whereas
allows a to be used outside of foo. In this second example, the global value of a will be updated by the function foo even if the function does not return a to the caller. (Notice that some optional ; separators were used in the above function definitions even though Lua does not require them).
Lua Functions work like functions in other languages. However, there are some unique features which are described here. A basic function requires a name and an end statement. An empty function that does nothing would look like this:
function foo() end
or, we could also write the same thing like this:
foo = function() end
These are equivalent declarations of a function in Lua. In both cases the script tells Lua that foo is a function and that it takes zero arguments. To make this function do something, we add some internal statements, like this:
You may wish to place functions in external files that are made available to the script at run time using the Include function. When the Include statement is processed, Lua executes the contents of the included file.
Lua allows function names to be passed to another function as an argument. Since Lua does no prototyping as in C and C++, the details of the passed function do not matter. Lua knows only that the argument list is passing a reference to a function.
One other comment about the preceding example: Notice that the object I is created inside the function foo. By default, Lua creates values that are global rather than local, which is the opposite of C and C++. Therefore, the value I can be returned to, and used by the calling function. To make I (or any other value) local to the function, use the local declaration; see Blocks and Scope, above.
A table is an associative set used for working with collections of objects of any type. This means that a value is stored in a table using a key, which may be a string, number, function, etc. For example, you can assign a value to a table using a syntax like this: T.key = value. You would then fetch the value like this: MyValue = T.key, which returns whatever was assigned as value, so that MyValue == value evaluates as true.
A table can be treated as an array of numbers, strings, other tables, or other things of any supported type. It may be used to create a data structure, a class, or a package of other tables. The Table construct is exceptionally versatile.
A table is created using a syntax like t = {}. What goes between the{} defines the members of the table. A table can include properties like a C languagestruct. The properties may be initialized when the table is constructed, as in T = { value1=12, value2="a string" }. Table properties are accessed using the dot (.) operator. For example, in this table, T.value1 begins with the value 12. To add members later, just use the syntax T.key = value or T[key] = value outside the {}.
A table be treated as an array by indexing its members at a numerical index using the [] operator with indices beginning at 1. The first member of table T can be indexed element can be indexed as an array using integral numbers beginning at index 1, as in [1], [2], etc. This is different from the C language protocol of indexing objects starting at 0. As an example, suppose we have 3 tablesa, b, and c. Then we could assemble them into a single super-table that is indexed like an array as follows:
Then, t[3][2] == "crane" evaluates true ( for true, see Special Values, below).
A table may also contain functions. A table function is accessed using the dot operator, as in T.MyFunction(). To assign functions to a table, do this:
--
--
In the example above, the -- refers to whatever is contained in the function body. To access these functions, use T.MyFunc() and so on.
As described in Classes, below, you can setup a table to be a class containing both data and functions. To access the functions you can then use either the dot operator or the colon operator (:).
The Lua table is so versatile that it can be used to create a class for use in object oriented programming. This capability is detailed in the book Programming in Lua. The dot operator may be used to access a table function when there is only a single instance of a particular table. However if you use a new operator as described in PIL, then you can create multiple instances of the same kind of table. Then, to tell Lua which instance the function refers to, you should use the colon (:) operator to access the class functions (but use the dot operator for class properties). For a class o in which a function foo has been declared, the following statements are equivalent:
As you can see, the colon operator avoids passing the object o for use by the function. The colon hides the use of a pointer to the class object, called the this or self pointer. Using the colon syntax allows the function foo to know who it belongs to, which means that it can work with other members of the same class. The C++ language does this too, but hides the mechanism while making a this pointer available if you need it. Lua allows you to access the table functions (you can also call them class methods) using either the dot operator and explicit self/this pointer, or simply use the colon operator.
Suppose we have a function foo that sets the value of a global variable v and returns its prior value (see Blocks and Scope about global variables):
This function could be added to a class using either the dot or colon operator but we will show only the case using the colon operator (:). The function is declared as a class member like this:
-- first, declare the class CClass
-- create an instance of the class CClass
-- use the instance C to do something
-- when finished with C, delete the instance
See Programming in Lua for more information about classes and object oriented programming in Lua.
Lua implements the concept of the C language NULL, which in Lua is called nil. The purpose of nil is to be special—that is, to be different from all other values. This value is useful for testing for the existence of objects or whether an object or value has actually been assigned. For example, if you test a value name before is is declared with a value, then the result is nil. Similarly, a function that creates an instance of a class might return nil if the class construction failed. For example,
This code fragment could also be written more compactly as
Lua includes a boolean type as the special values true and false. Anything non-zero evaluates true but a function might return the explicit values true or false which can be tested as such.
The keyword not provides negation, so that the statement false == not true evaluates true.
You do not need to declare variables, even for strings. Lua performs dynamic typing, which means that it determines the type of value you want at the time that something is assigned to it. If you assign a numeric value or expression to a token, then the token becomes a number. Strings work the same way. In addition, if you create a new instance of a class, the new method returns a reference to the class object in memory, which you assign to a token so that the script can access the class members.
Since Lua also implements the Table concept as a data structure like a C struct or array, dynamic typing also works with complex objects to make your scripts more capable and easier to debug. For example, you can create a function in the script and the function can create a class object, such as a CImage. The function returns the instance of the CImage as its value, which is assigned to a token in the calling script. The calling script can work with that object thereafter, including deleting it at some point. The calling script does not need to know in advance that the statement calling the function returns a reference to a class object; the calling script learns about the type of the reference when the script actually uses it. For example, here is a class version of a function foo:
|
-- function takes one parameter |
|
-- create an image object |
|
-- try to open a file from path s |
|
-- exit the script |
|
|
|
-- return a reference to the object |
|
-- end of the function |
|
|
|
-- return a CImage and assign to I |
|
-- test that I is valid (non 0) |
|
-- print the number of rows in the image |
|
-- done using the object Image |
|
|
In the above sample script, a CImage object is created within a separate function that loads an image from a file. Since I is not declared as local inside foo, it is global and can be accessed by the calling function (see Blocks and Scope, above). The value I is returned as a matter of programming style, to make clear that it was created by foo. If foo did not return the value of I, the calling function still could use I but the code would be more obscure because it would not be clear exactly where I came from.
The Lua language contains only a minimum number of math functions such as min and max. This was done to keep the language maximally portable across computing platforms. Instead, Lua provides a rich collection of math functions in the math package. These packages are actually Lua tables containing functions. Therefore, you access these math functions using the . syntax, as in math.sqrt() or math.log(). This package is described in the Lua documentation. When you run a script, Mira automatically includes the math package functions.
Lua provides a string package with a rich assortment of string processing functions, an io package containing input/output functions, and other packages described in the Lua documentation. When you run a script, Mira automatically includes the string and io package functions.
Mira MX Script includes string functions for string formatting, including Printf and Sprintf. These provide C-like string formatting for numbers or different types, strings, pointers, and hexadecimal values.
The MX Script Printf function works like the C printf function by sending formatted text to an output device. MX Script sends this formatted string to a Mira Text Editor window. There is also a Printf method in the provided CTextView class.
The MX Script Sprintf function works like the C sprintf function by formatting to a string. However, Sprintf takes the format string as its first argument and returns the formatted string that must be assigned to a value. For example, str = Sprintf( "%s, %d, %lg", a, b, c ) formats the values a, b, and c and assigns the resulting string to the value of str . You do not need to declare str as a string before it is assigned a value.
The C and C++ languages provide a conditional block structure of the form
if {--} else if {--} else {--}
where the {} are used when more than one statement is used. Lua provides the same functionality using the syntax
if -- then -- elseif -- then else -- end
where there may be any number of elseif blocks. Lua parses the statements in the -- without any {} style delimiters, regardless of whether there are 1 or 1000000 statements involved. If it makes the script easier to understand, you can use () around the -- in the initial condition, as in if ( ... ) then. Lua also allows you to place the entire block on any number of lines. For example, you can do it this way:
|
|
|
|
|
It is often easier to put a short if block inline, as shown below. Here is the same block written inline and without parentheses:
Lua provides 3 types of loops: for, while and repeat. The for and while loops terminate with an end statement, but the repeat loop terminates when an until condition statement.
The for loop uses rigid limits that cannot be changed. For example,
-- code goes here
This loop runs with values of i = 2, 6, 10, and 14. It cannot test a stopping condition in the same way as a C-style for loop. However, code inside the loop can perform some kind of test and may exit the loop early using a break or return statement.
The for loop offers another syntax that is useful in iterating through a table, as in for k, v in a do. For details, see Programming in Lua.
The while loop uses the keyword do to test a condition at the beginning, as in
-- code goes here
This loop runs until the condition a < 10 fails. The while loop can run 0 times if the condition fails immediately when the loop is entered.
The repeat loop is similar to a while loop but tests its condition at the end of the loop. A repeat loop runs at least 1 time. For example,
-- code goes here
The parentheses around the until condition are optional.
A Lua function can return a value just like a C function. However, a Lua function can return 0 or more values. For example, the following syntax is valid in Lua:
|
-- return the limits of a CRect object R |
This bit of code returns 4 numbers that are the limits of a rectangle in the CRect class.
One caveat for using multiple returns is that, if a function with multiple returned values is used as an argument to another function, then only the first returned value is used by the function, unless that function is the only argument. Therefore, we have 3 cases to consider:
|
-- returns 4 value for use by the script |
|
-- prints the values of a and x |
|
-- prints all 4 values for a, b, c, and d |
Using multiple returns is handy when you may want a general function to return, say, a value and a status flag, but sometimes you want to use only the returned value and other times you want to use both the returned value and the status.
Lua uses the .. operator to concatenate strings. For example, suppose the value of x is 10.5. Then
produces s = "abc:10.500". Notice that the value returned by Sprintf is a string.
Mira MX Script provides a capability for adding WYSIWYG strings to your programs using Lua's literal string capability. A literal string is processed to look exactly the way you wrote it in the Script Editor, including line breaks, spaces, and tabs. To create a literal string, simple enclose the literal region between the tokens [[ and ]]. Lua interprets the literal string as everything between the [[ and ]] when the chunk is compiled at run time. Lua does not enter these tokens into the resulting string; it just uses[[ and ]] to switch-on and switch-off the literal string interpreter.
In the following example, we use the Printf function to format some text which we want to appear a certain way in the Mira Text Editor window. Part of the string formatting uses static text as laid out, the other part involves formats like %s and %d that are filled in at run time. Writing the source code to get this right without using a literal string would certainly be a lot of work! Everything between the [[ and ]] prints exactly as written:
-- after the comma, enter the parameter list as normal
Note that the literal string includes new lines and offsets from the left margin. Therefore, if you used the script exactly as shown above, each printed line would show a tab offset from the left margin. The format string above does not include new line characters. If you did not use a literal string, the format would need to include \r\n at each point where you want a line break to occur.