You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.3 KiB
Lua

--[[---
Easy no-fuss modules.
Any function inside the module will be exported, unless it is explicitly
local. The functions are declared up front using patching, leading to efficient
calls between module functions.
require_ 'module'
function one ()
return two()
end
function two ()
return 42
end
Classes can also be declared inside modules:
require_ 'module'
class A
function set(self,val) @val = val end
function get(self) return @val end
end
Within class definitions, the macro `@` expands to either `self.` or `self:` depending
on context, and provides a Ruby-like shortcut.
If you give these modules names with `m.lua` extension like `mod.m.lua`, then you can
simply use `require()` to use them with LuaMacro.
@module macro.module
]]
local M = require 'macro'
local locals, locals_with_value = {}, {}
local ref
local function module_add_new_local (name)
locals[#locals+1] = name
end
local function module_add_new_local_with_value (name,value)
locals_with_value[name] = value
end
local function was_local_function (get)
local tl,keyw = get:peek(-1)
return tl=='keyword' and keyw=='local'
end
-- exclude explicitly local functions and anonymous functions.
M.keyword_handler('function',function(get)
local tn,name = get:peek(1)
local was_local = was_local_function(get)
if not was_local and tn == 'iden' then
module_add_new_local(name)
end
end)
-- when the module is closed, this will patch the locals and
-- output the module table.
M.keyword_handler('END',function(get)
local concat = table.concat
local patch = ''
if next(locals_with_value) then
local lnames,lvalues = {},{}
local i = 1
for k,v in pairs(locals_with_value) do
lnames[i] = k
lvalues[i] = v
i = i + 1
end
patch = patch..'local '..concat(lnames,',')..'='..concat(lvalues,',')..'; '
end
if #locals > 0 then
patch = patch .. 'local '..concat(locals,',')
end
get:patch(ref,patch)
local dcl = {}
for i,name in ipairs(locals) do
dcl[i] = name..'='..name
end
dcl = table.concat(dcl,', ')
return 'return {' .. dcl .. '}'
end)
local no_class_require
-- the meaning of @f is either 'self.f' for fields, or 'self:f' for methods.
local function at_handler (get,put)
local tn,name,tp = get:peek2(1)
M.assert(tn == 'iden','identifier must follow @')
return put:iden ('self',true) (tp=='(' and ':' or '.')
end
local function method_handler (get,put)
local tn,name,tp = get:peek2()
if not was_local_function(get) and tn == 'iden' and tp == '(' then
return put ' ' :iden ('_C',true) '.'
end
end
M.define ('_C_',function()
M.define_scoped('@',at_handler)
if not no_class_require then
module_add_new_local_with_value('_class','require "macro.lib.class"')
no_class_require = true
end
M.scoped_keyword_handler('function',method_handler)
end)
M.define('class',function(get)
local base = ''
local name = get:iden()
if get:peek(1) == ':' then
get:next()
base = get:iden()
end
module_add_new_local(name)
return ('do local _C = _class(%s); %s = _C; _C_\n'):format(base,name)
end)
-- the result of the macro is just a placeholder for the locals
return function(get,put)
ref = get:placeholder(put)
return put
end