diff options
author | chai <chaifix@163.com> | 2021-11-17 23:03:07 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2021-11-17 23:03:07 +0800 |
commit | 27d6efb5f5a076f825fe2da1875e0cabaf02b4e7 (patch) | |
tree | 44f301110bc2ea742908ed92a78eba0803cd3b60 /Tools/LuaMacro/macro/module.lua | |
parent | b34310c631989551054d456eb47aaab5ded266a4 (diff) |
+ LuaMacro
Diffstat (limited to 'Tools/LuaMacro/macro/module.lua')
-rw-r--r-- | Tools/LuaMacro/macro/module.lua | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/Tools/LuaMacro/macro/module.lua b/Tools/LuaMacro/macro/module.lua new file mode 100644 index 0000000..a8e1b8d --- /dev/null +++ b/Tools/LuaMacro/macro/module.lua @@ -0,0 +1,132 @@ +--[[--- +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 + + |