lua-users home
lua-l archive

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


Thomas Lauer <thomas.lauer <at> virgin.net> writes:
> ...I experimented with an extension to the parser to
> enable the declaration of globals at compile time, similar to what the
> local keyword does for local variables....
> However, I've run into a problem ...module() is a runtime feature and
> the compiler knows nothing about it

True, require/module will need to be a compile time construct.

I've updated LuaFish[1] (which was recently announced on this list) to do
something like this.  Again, it's quite preliminary, but one of the included
examples is shown below.

First, a simple module is defined as follows:

<snip>
-- msquare.lua

local M = {}; M.__index = M

function M.create(length)
  local self = setmetatable({length = length}, M)
  return self
end

local function area(self) return self.length^2 end
M.area = area

local function perimeter(self) return self.length * 4 end
M.perimeter = perimeter

local function setcolor(self, color)
  SETTYPE(color, 'string') -- static type check argument
  self.color = color
end
M.setcolor = setcolor

-- The function inside the ONCOMPILE macro is executed at
-- compile time.
ONCOMPILE(function(scope) -- scope is a table of references to the
                          -- AST's of lexicals at the current
                          -- code location
  -- Static type definition of the class.
  local class = {}
  class.area = scope.area  -- use static function signatures
  class.perimeter = scope.perimeter
  class.setcolor = scope.setcolor

  registerclass('Square', class)
end)

return M
</snip>


The function in the "ONCOMPILE" macro gets executed at what is effectively
compile time.  This particular function builds a table of method names and their
signatures, which makes up the static type of a class that we register.

Next, another Lua program uses that module as follows:

<snip>
-- module_usage_m.lua
-- demonstrates static checking of class methods.

-- The function inside the ONCOMPILE macro is executed at
-- compile time.
ONCOMPILE(function()
  -- load type definitions at compile time to allow
  -- compile-time type checking.
  mrequire 'square_m'
end)

print 'begin'

local mrequire = require 'LuaFish.macro'.require

local Square = mrequire 'square_m'

local m = Square.create(5)
SETTYPE(m, 'Square') -- bind a static type to the lexical

assert(m:area() == 25) -- ok
assert(m:perimeter() == 20) -- ok
m:setcolor('blue') -- ok

-- this gives compile-time error:
--   "method volume absent in class Square"
-- print(m:volume())

-- this gives compile-time error:
--   "argument color type is number but expecting string"
-- m:setcolor(5)

print 'done'
</snip>

Doing the require inside the "ONCOMPILE" macro causes the types and method names
to be loaded at compile time.  These types are then bound to a lexical at
compile time via the SETTYPE macro.  This then permits compile-time detection of
the errors noted in the commented code at the end.

The ONCOMPILE macro is actually similar to the "BEGIN" block in Perl.  Putting a
require inside an ONCOMPILE blocks is analogous to using a "use" [2] v.s.
"require" in Perl.

[1] LuaFish v. 0.2 - http://lua-users.org/wiki/LuaFish
[2] Perl "use" statement - http://perldoc.perl.org/functions/use.html