Sunday 12 March 2017

Finding a Hyphenated Sub-String of a String in Lua

The Lua string module has a find function which locates a specified sub-string with a longer string. It seems intuitive that the function looks for a string within a string.

    > str = 'A string containing abc-def amongst other things'
    > =string.find(str, 'amongst')
    29    35

However, what isn't clear from that example that the second argument to Lua's find function is not a simple string but a pattern. A '-' is a "reserved" symbol in a Lua pattern so if you simply search for a string containing one, you will most probably not find it.

    > =string.find(str, 'abc-def')
    nil

The '-' character  in a pattern means "Match the previous character (or class) zero or more times, as few times as possible." It can be "escaped" with the '%' character.

    > =string.find(str, 'abc%-def')
    21   27

Alternatively, the find function accepts a fourth parameter which turns off pattern matching and your search will return the expected result. (In order to use it, you must also supply the third parameter which tells the find function where to start its search).

    > =string.find(str, 'abc-def', 1, true)
    21        27


Sunday 5 March 2017

Lua Error Handling

Like many things in Lua, error handling is simple and straightforward and a little different from many languages. I am writing these simple notes to act as a reminder of those differences.  

There are two functions in Lua that allow you to trap errors, pcall and xpcall. The 'p'  seems to stand for protected, I haven't been able to make a good guess as to the 'x'. The difference between the two is that xpcall allows you to supply your own error handling function.

The main difference is that pcall and xpcall are functions rather than statements. You need to pass the code you wish to protect to them as an argument. You can't just wrap your code in try blocks as you can in many languages. You can't pass code blocks (chunks in Lua terminology) as function arguments in Lua. You need to wrap the code in a function. (It can be anonymous.)

A consequence of this is that you need to remember to pass the function and not the result of the function. A mistake that I found easy to make:
    > function f() return 1 end
    > =pcall(f())
    false attempt to call a number value

It should have been:
    > =pcall(f)
    true 1

You have probably noticed that pcall returns multiple values, the first is a status (true for okay, false if an error occurred) and then either the values returned from your function or from the error function. For example:
    > function fe() error('there was an error') end
    > =pcall(fe)
    false stdin:1: there was an error

You pass arguments to your function by supplying them as additional arguments to pcall:
    function errDemo (i, j) 
      if 'number' ~= type(i) or 'number' ~= type(j) then 
        error{msg='catch this'}
      end
      return i, j
    end

    > =pcall(errDemo, 1, 2)
    true       1 2
    > status, err = pcall(errDemo, "1", 2)
    > =status
    false
    > =err.msg
    catch this

Lastly, xpcall takes an additional function, passed as second argument, that will be called when an error occurs. It is passed object that is returned by the error function. Here is a simple example:
    > function handleError (err) return 'caught you' end
    > =xpcall(errDemo, handleError, "1", 2)
    false caught you