Calico Lua

From IPRE Wiki
Jump to: navigation, search

Calico Lua is based on LuaSharp by Josh Simmons.

Calico Lua currently has the following limitations:

  • Standard Calico modules and libraries (like Myro and Graphics) are not accessible
  • cprint will print to the console
  • On Windows, Calico Lua will currently print to the console, and there is no cprint function

See the examples in the examples subdirectory.

Getting Started with Calico Lua

These are some notes, especially useful for those programmers already familiar with Python or another modern language.

1. Lua interpreters do not allow general expressions, like Python. For example, in Python you can type:

python> 1 + 1         # or any valid Python expression
2

and thus use the command entry area as an expression evaluator. This does not work the same in Lua. You need to either print an expression:

lua> print(1 + 1);
2

Or, you can use a special interpreter form starting with an equals sign:

lua> = 1 + 1;
2

This does not work in Lua:

lua> 1 + 1;             --- Not valid in Lua

2. Like Python, the ending semi-colon is optional in Lua. However, it is sometimes needed to prevent ambiguous meaning. In Lua, you can use semicolons or commas between list items. We will always reserve semicolons for end of statements, and commas between items.

For example, consider this code:

a = f
(g).x(a)

This isn't ambiguous to Lua; it means: "a = f(g).x(a)" which is not what we intended judging from the line break. But Lua doesn't see line breaks. Thus a semicolon is required to get the proper meaning:

a = f;
(g).x(a)

3. Sometimes commas are optional (in table constructors) and sometimes not (everywhere else):

lua> print(1,);      --- Not valid
lua> print{1,};      --- Valid
lua> x = a,         --- Not valid
lua> x = a,;         --- Not valid

4. Lua expression evaluation is sometimes confused with syntactic parsing. For example, many languages (Python and Scheme, for example) would allow these both:

lua> x = {}; print(x.not_defined_yet); -- Valid in Lua
lua> print({}.not_defined_yet);        -- Not valid in Lua; why not?!

5. Lua starts counting table items using 1 (rather than the standard 0):

lua> x = {7, 8, 9};
lua> print(x[1]);
7

This is a bad idea. http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html

6. Lua table counting skips over nils. For example:

lua> x = {1, 2, 3, nil, nil, nil, 4};
lua> print(#x);                             --- #x is the length operator
7
lua> x = {1, 2, 3, nil, nil, nil, 4, nil};
lua> print(#x);                             --- #x is the length operator
3

OMG. This is a language that people use to productively do things, but has many issues and shows that the design did not into account various opinions from a diverse group.

Lua has a work around for this issue:

lua> x = {1, 2, 3, nil, nil, nil, 4, nil};
lua> table.maxn(x);
8

The table variable has some useful functions, we'll see later.

7. This is just ugly and hard to guess what is going on, but it was designed to work like this:

function f() return 1,2,3 end
print(f());                         ---> 1 2 3
function g() return 4,5,6 end
print(g());                         ---> 4 5 6
print(f(), g());                    ---> 1 4 5 6

Lua discards all that a function returns, except for the first item, except if the function call is the last in a list, and then it uses all.

8. Somethings are not defined until you load a file from another file, using require/module:

lua> require("myfile");

And in myfile.lua you might have:

module(..., package.seeall);

... (that is, dot dot dot) is a shorthand for "the values that you are passing me". This is not properly defined until it is called. That is, if you load a file in Calico, ... won't be properly defined.

9. Calico Lua will display the contents of a table, rather than just its type or memory location:

Standard Lua> = {1, 2, name = "Betty", 3}
table: 0x1b51190
Calico Lua> = {1, 2, name = "Betty", 3}
{1, 2, 3, name=Betty}

Introduction to Lua

Lua is not sensitive to indentation or newlines, which is very different from Python. In our examples, however, we will write Lua code in the same manner that Python requires, just as a matter of style.

Variables

lua> x = 12;
lua> gravity_on_earth = 9.8;

Any variable that hasn't been defined has a value of "nil":

lua> print(not_defined_yet);
nil

Data Structures: the table

In Lua, there is really only one data structure: the table. Perhaps it is best seen through some examples:

First, the table is made using the curly braces. You can use this to make lists of any kind of items:

lua> mylist1 = {}
lua> mylist2 = {1, 2, 3, 4, 5}
lua> mylist3 = {"one", 2, "three", 4, "five"}

One big difference between Lua and most other modern languages is that you refer to items by their position in a list, rather than their offset from the beginning. You may not have realized in Python when you refer to the first item in a list with "list[0]" that zero is an offset from the beginning. There are reasons for choosing such zero-based numbering scheme, but Lua went a different route, and use a one-based positional notation. So:

lua> print(mylist3[1]);
one

This can be confusing, but we'll write lots of test programs to make sure that our code works as planned.

You can also use tables to make Dictionaries (sometimes called hash tables, or association lists):


lua> person1 = {name="Alice", phone="6754"}
lua> person2 = {name="Bob", phone="7432"}

You can use two ways of referring to these parts:

lua> print(person1.name);
Alice

or

lua> print(person1["name"]);
Alice

Both of these may look familiar to the Python programmer.

NOTE: you can use semicolons instead of commas throughout Lua; however, we will reserve their use for end of lines only.

Like with variables, names in table are "nil" until defined:

lua> = person1.address
nil

You can add things to a table in this manner as well:

person1.surname = "Smith"

Functions

lua> myfunc = function () return 42 end;

or better formatted:

myfunc = function () 
             return 42;
         end;

This is called in a standard manner:

lua> myfunc()

A little bit of syntactic sugar makes function definitions easier to read:

function myfunc () 
    return 42;
end;

However, this is identical to the above definition.

Lua is quite permissive in how you call functions:

  • If you have one or more arguments, the parens are optional
  • The number of parameters and arguments don't have to agree; Lua will fill in with nil for missing arguments and will ignore extra ones

Examples:

function double (n)
    return n * 2
end
print( double(21) );    --> 42
print( double(21, 34, 56, 67, 78) );    --> 42, and ignores all the rest

... is a specially named variable that holds all of the arguments passed in:

function say_hello (...)
    for k,v in ipairs{...} do
        print("Hello, " .. v);
    end
end
say_hello("Apple");               --> prints "Hello, Apple"
say_hello("Banana", "Orange");    --> prints "Hello, Banana"
                                  --> prints "Hello, Orange"

Using Tables to implement Objects

Lua does not have an object or module system: it only has tables. But tables can be used as modules and objects. For example, consider this:

utilities = {}
function utilities.add1 (number)
    return number + 1
end

If you have that bit of code defined, then you can use it as so:

lua> = utilities.add1(41)
42

Let's now look at the development of real classes, with methods, and inheritance.

Consider a class Vector with fields x and y.

Vector = {}

function Vector.new (x, y)
    local vector = {}
    vector.x = x;
    vector.y = y;
    return vector;
end

v1 = Vector.new(0, 0)
v2 = Vector.new(10, -4)

v3 = Vector.new(v1.x + v2.x, v1.y + v2.y)

That will create two "Vector" objects (v1 and v2), and add them together to create a new v3.

But what if we would like to have real methods in these objects? Say, if we would like to move the add vector logic into the Vector class?

We know we can add functions to the Vector namespace (as shown above):

Vector = {}

function Vector.new (x, y)
    local vector = {}
    vector.x = x;
    vector.y = y;
    return vector;
end

function Vector.add(v1, v2)
     return Vector.new(v1.x + v2.x, v1.y + v2.y)
end

v1 = Vector.new(0, 0)
v2 = vector.new(10, -4)

v3 = Vector.add(v1, v2)

But this doesn't allow the standard use:

v1 = Vector.new(0, 0)
v2 = vector.new(10, -4)
v1.add(v2)

Notice that we would like to write v1.add(v2) or even v1 + v2. Instead we had to write the verbose Vector.add(v1, v2).

To do this in a better way, we need to have access to the internals of v1 inside the add method. Actually, we can't do this! But, we can add a little syntactic sugar to make this look better.

Vector = {}
Vector.__index = Vector                         --> Allows vector.add to find add in Vector

function Vector.new (x, y)
    local vector = {}
    setmetatable(vector, Vector)                --> Allows the look up of __add to be found in Vector
    vector.x = x;
    vector.y = y;
    return vector;
end

function Vector.__add (self, v2)
     return Vector.new(self.x + v2.x, self.y + v2.y);
end

function Vector.add (self, v2)
     return Vector.new(self.x + v2.x, self.y + v2.y);
end

v1 = Vector.new(0, 0)
v2 = Vector.new(10, -4)

v3 = v1 + v2
v4 = v1.add(v1, v3)

That is close to what we want.

The last little bit of syntactic sugar is to hide the defining of "self", and the passing of "self" by using the colon operator:

Vector = {}
Vector.__index = Vector                         --> Allows vector.add to find add in Vector

function Vector.new (x, y)
    local vector = {}
    setmetatable(vector, Vector)                --> Allows the look up of __add to be found in Vector
    vector.x = x;
    vector.y = y;
    return vector;
end

function Vector:__add (v2)
     return Vector.new(self.x + v2.x, self.y + v2.y);
end

function Vector:add (v2)
     return Vector.new(self.x + v2.x, self.y + v2.y);
end

v1 = Vector.new(0, 0)
v2 = Vector.new(10, -4)

v3 = v1 + v2
v4 = v1:add(v3)

That looks perfect! Or as good as we can make it in Lua.

Helpful Tricks

In Python, you can use introspection to see what is available. For example, you can use the dir() in Python. In Lua, you can look at an item's metatable:

lua> s = "Hello World"
Ok
lua> = s
Hello World
lua> = getmetatable(s)
{__index={...}}

In this example, we make a string, s, and look at its metatable to see what other properties it has. It only has __index, which is itself a table. The __index is used to lookup any name that isn't defined in s's metatable. We can get the __index metadata:

lua> = getmetatable(filename).__index
{sub=LuaSharp.LuaFunction, upper=LuaSharp.LuaFunction, len=LuaSharp.LuaFunction, 
gfind=LuaSharp.LuaFunction, rep=LuaSharp.LuaFunction, find=LuaSharp.LuaFunction, 
match=LuaSharp.LuaFunction, char=LuaSharp.LuaFunction, dump=LuaSharp.LuaFunction, 
gmatch=LuaSharp.LuaFunction, reverse=LuaSharp.LuaFunction, byte=LuaSharp.LuaFunction, 
format=LuaSharp.LuaFunction, gsub=LuaSharp.LuaFunction, lower=LuaSharp.LuaFunction}

which shows that there are a number of methods that all strings have: sub, upper, len, etc. You can use them like so:

lua> = s:sub(2, 4)
http

Note that you must use the colon operator (rather than period) as these are written as methods with the self parameter. Otherwise, you could write:

lua> = s.sub(s, 2, 4)
http

If you accidentally use the dot without self, you will get no error, and no results:

lua> = s.sub(2, 4)

Ok

What happens? It turns 2 into a string, then takes the 4th character onward of that string ("2"). For example:

lua> = s.sub(1234, 2)
234

So, be careful with dots vs colons!