lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi all,

This is the second beta release of LuaMacro 2, yet another Lpeg-driven
syntactical candy store for Lua. A few bugs squished (like 'elseif'
not been recognized as a keyword) and some nice generalizations: for
instance, you can define new tokens (like '@@') and use them as
macros. Operator macros can be pass-through, so that you can safely
overload standard Lua tokens like '{' and '.'.

The front-end luam also sets a custom package loader, as suggested by
Alexander. So if require 'fred' fails, the package loader will look
for fred.m.lua and preprocess and load that.

There is an interactive prompt:

$> luam -i
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
Lua Macro 2.1 Copyright (C) 2007-2011 Steve Donovan
> require 'macro.all'
> forall i in {10,20,30} do print(i) end
10
20
30
> f = \x(x+1)
> = f(10)
11

https://github.com/stevedonovan/LuaMacro

There was recent discussion around new control structures, and I still
think that a macro facility provides the best laboratory for
experimentation.  For instance, a simple case statement is a one-line
macro:

def_ case(x) do def_ (of elseif _value ==) local _value = x if false
then _END_END_

function test(n)
    local res
    case(n)
    of 10 then
        res = 1
    of 20 then
        res = 2
    else
        res = 3
    end
    return res
end

The 'of' is simply defined as an elseif (but notice that it is defined
within the scope of the case macro, and will _not_ exist outside the
case block)  The _END_END_ is a built-in macro that ensures that the
final 'end' causes another 'end' to be emitted and closing the do
block. (So we don't have to do unnatural things like 'endcase')

Here is a 'with' statement

with aLongName do
   .x = 1
   .y = 2
end

Note the periods; we could fool with dynamic scope at this point (and
macros can smooth over the differences here between 5.1 and 5.2) but
personally I was burnt too often by the Pascal with statement; this is
one of the few places where Visual Basic syntax is better, IHMO.

The idea is to look at the token just before each period and decide
whether we should insert a table reference before it. Basically, if
the last token is not a name or ']' then we go ahead.  Everything is
put inside a do block with a local alias to the expression.

M.define('with',function(get,put)
  M.set_scoped_macro('.',function()
    local lt,lv = M.last_token() --  peek before the period...
    if lt ~= 'iden' and lt ~= ']' then -- complete the table lookup
      return '_var.'
    else
      return nil,true -- pass through
    end
  end)
  local expr = get:upto 'do'
  return 'do local _var = '..tostring(expr)..'; '
end)

I agree that this is a little bit hacky. But the new meaning of '.'
again does not escape the with block so it cannot do arbitrary damage
elsewhere.

It's now possible to completely re-skin Lua - an example is here:

https://github.com/stevedonovan/LuaMacro/blob/master/tests/test-cskin.lua

Basically, cskin.lua defines a simple curly-braces version of Lua with
OOP in about 140 lines.

Personally, I think it's a bit scary and overboard. But it was an
interesting technical challenge ;)  As always, don't use excessive
syntactical sugar if on a kilocalorie-restricted diet. Ask your health
professional if in doubt.

steve d.