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
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
|
|
|
|
|