diff options
Diffstat (limited to 'Tools/LuaMacro/tests')
46 files changed, 1708 insertions, 0 deletions
diff --git a/Tools/LuaMacro/tests/cexport.lua b/Tools/LuaMacro/tests/cexport.lua new file mode 100644 index 0000000..cb20bd0 --- /dev/null +++ b/Tools/LuaMacro/tests/cexport.lua @@ -0,0 +1,77 @@ +local M = require 'macro' + +local cf,copy,null +if package.config:sub(1,1) == '\\' then + cf = 'fc'; copy = 'copy'; null = ' > null' +else + cf = 'diff'; copy = 'cp'; null = ' > /dev/null' +end + +local f,hname,mname + +M.keyword_handler('BEGIN',function() + hname = M.filename:gsub('%.%a+$','')..'.h' + mname = hname:gsub('%.','_'):upper() + f = io.open(M.filename..'.h','w') + f:write('#ifndef ',mname,'\n') + f:write('#define ',mname,'\n') +end) + +M.keyword_handler ('END',function() + f:write('#endif\n') + f:close() + local tmpf = M.filename..'.h' + if os.execute(cf..' '..hname..' '..tmpf..null) ~= 0 then + os.execute(copy..' '..tmpf..' '..hname..null) + end +end) + +M.define('export',function(get) + local t,v = get:next() + local decl,out + if v == '{' then -- block! + decl = tostring(get:upto '}') + decl = M.substitute_tostring(decl) + f:write(decl,'\n') + else + decl = v .. ' ' .. tostring(get:upto '{') + decl = M.substitute_tostring(decl) + f:write(decl,';\n') + out = decl .. '{' + end + return out +end) + + +--[[ + +Example of a with-statement: + + with(MyType *,bonzo) { + .x = 2; + .y = 3; + with(SubType *,.data) { + .name = "hello"; + .ids = my.ids; + printf("count %d\n",.count); + } + } + + +M.define('with',function(get) + get:expecting '(' + local args = get:list() + local T, expr = args[1],args[2] + get:expecting '{' + M.define_scoped('.',function() + local lt,lv = get:peek(-1,true) -- peek before the period... + if lt ~= 'iden' then + return '_var->' + else + return nil,true -- pass through + end + end) + return '{ ' .. tostring(T) .. ' _var = '..tostring(expr)..'; ' +end) + +]] diff --git a/Tools/LuaMacro/tests/class1.lc b/Tools/LuaMacro/tests/class1.lc new file mode 100644 index 0000000..60a73a0 --- /dev/null +++ b/Tools/LuaMacro/tests/class1.lc @@ -0,0 +1,40 @@ +// preprocess using luam -C -llc class1.lc > class1.c +module "class1" { + +class A { + int handle; + + constructor (Int i) { + this->handle = i; + } + + def geth () { + lua_pushinteger(L, this->handle); + return 1; + } + + def __eq(A other) { + lua_pushboolean(L, this->handle == other->handle); + return 1; + } +} + +def A (Int i) { + push_new_A(L,i); + return 1; +} + +constants { + Int MAGIC = 42 +} + +} + +lua_tests { + require 'class1' + M = class1.MAGIC + o = class1.A(M) + assert(o:geth() == M) + a = class1.A(M) + assert(a == o); +} diff --git a/Tools/LuaMacro/tests/const.lua b/Tools/LuaMacro/tests/const.lua new file mode 100644 index 0000000..f5f8508 --- /dev/null +++ b/Tools/LuaMacro/tests/const.lua @@ -0,0 +1,9 @@ +local macro = require 'macro' +macro.define ('const',function(get) + get() + local vars,values = get:idens '=',get:list '\n' + for i,name in ipairs(vars) do + macro.assert(values[i],'each constant must be assigned!') + macro.define_scoped(name,tostring(values[i])) + end +end) diff --git a/Tools/LuaMacro/tests/cskin.lua b/Tools/LuaMacro/tests/cskin.lua new file mode 100644 index 0000000..d77f7f9 --- /dev/null +++ b/Tools/LuaMacro/tests/cskin.lua @@ -0,0 +1,127 @@ +-- cskin.lua +local M = require 'macro' + +M.define_tokens{'&&','||','!=','{|'} + +M.define '&& and' +M.define '|| or' +M.define '!= ~=' +M.define '! not' + +local push,pop = table.insert,table.remove +local bstack = {} +local btop = {} + +local function push_brace_stack (newv) + newv = newv or {} + newv.lev = 0 + push(bstack,btop) + btop = newv +end + +local disabled = false + +-- skinning can be temporarily disabled; this macro re-enables skinning +M.define('_SKIN_',function() disabled = false end) + +local function block_statement (word,end_handler) + return function(get,put) + if disabled then return end + get:expecting '(' + local stuff = get:upto ')' + get:expecting '{' + push_brace_stack {handler = end_handler} + return put:space():tokens(stuff):space():keyword(word) + end +end + +local function endif_handler (get,put) + local t,v = get:next() + if t == 'keyword' and v == 'else' then + local nt,nv = get:next() + if nt == 'keyword' and nv == 'if' then + return put:keyword 'elseif' + else + M.assert(nt == '{') + end + push_brace_stack() -- opening else block + return put:keyword 'else' + else + return put:keyword 'end' (t,v) + end +end + +M.keyword_handler ('for',block_statement 'do') +M.keyword_handler ('while',block_statement 'do') +M.keyword_handler ('if',block_statement ('then',endif_handler)) +M.keyword_handler ('elseif',block_statement ('then',endif_handler)) + +M.define('{',function() + if btop.lev then + btop.lev = btop.lev + 1 + end + return nil,true --> pass-through macro +end) + +M.define('}',function(get,put) + if btop.lev == 0 then + local res + if btop.handler then res = btop.handler(get,put) end + if not res then res = put:space():keyword'end' end + btop = pop(bstack) + return res + else + btop.lev = btop.lev - 1 + return nil,true --> pass-through macro + end +end) + +-- def for function is a little easier; it's a plain macro, +-- and we deliberately don't want to get rid of the parens. +M.define('def',function(get,put) + local stuff = get:list('{','') + put:keyword('function') + if btop.classname then put:iden(btop.classname) '.' end + push_brace_stack() + return put:list(stuff) +end) + +----- OOP support ------ +M.define('class',function(get,put) + local name,t,v = get:iden(),get:next() + local base = '' + if t == ':' then base = get:iden(); t = get:next() end + M.assert(t == '{','expecting {') + push_brace_stack {classname = name} + return 'do '..name..' = class_('..base..')' +end) + +class_ = require 'macro.lib.class' + +---- making an existing macro play nicely with the C skin --- +require 'macro.forall' + +local fun = M.get_macro_value 'forall' +M.define('forall',function (get,put) + get:expecting '(' + local stuff = get:upto ')' + get:expecting'{' + stuff:space():keyword 'do' + stuff = fun(get.from_tl(stuff),put) + -- let the unskinned Lua pass through with _SKIN_ to re-enable skinning + disabled = true + stuff:iden '_SKIN_':space() + push_brace_stack() + return stuff +end) + +-- often easier to express the macro in a skinned form +M.define('{|',function(get,put) + local expr = tostring(get:upto '|') + local select = tostring(get:upto '}') + return ('(def(){local r={}; forall(%s) {r[#r+1]=%s} return r})()'):format(select,expr) +end) + + + + diff --git a/Tools/LuaMacro/tests/dll.c b/Tools/LuaMacro/tests/dll.c new file mode 100644 index 0000000..629feae --- /dev/null +++ b/Tools/LuaMacro/tests/dll.c @@ -0,0 +1,37 @@ +// luam -C -lcexport dll.c +// expands this file into pukka C and creates/updates dll.h + +#include "dll.h" + +export { +typedef struct { + int ival; +} MyStruct; +} + +// yes we could use #define here, but it's sometimes useful to have another level +// of macro substitution +def_ alloc(T) (T*)malloc(sizeof(T)) + +// Plus, LuaMacro can do operator replacements. This is Ruby-style 'field' access +def_ @ self-> + +export MyStruct *create() { + return alloc(MyStruct); +} + +def_ This MyStruct *self + +export int one(This) { + return @ival + 1 +} + +export int two(This) { + return 2*@ival; +} + +export void set(This,int i) { + @ival = i; +} + + diff --git a/Tools/LuaMacro/tests/dollar.lua b/Tools/LuaMacro/tests/dollar.lua new file mode 100644 index 0000000..954416d --- /dev/null +++ b/Tools/LuaMacro/tests/dollar.lua @@ -0,0 +1,22 @@ +local macro = require 'macro'
+
+macro.define('$',function(get)
+ local t,v = get()
+ if t == 'iden' then
+ return 'os.getenv("'..v..'")'
+ elseif t == '(' then
+ local rest = get:upto ')'
+ return 'eval("'..tostring(rest)..'")'
+ end
+end)
+
+return function()
+ return [[
+local function eval(cmd)
+ local f = io.popen(cmd,'r')
+ local res = f:read '*a'
+ f:close()
+ return res
+end
+]]
+end
diff --git a/Tools/LuaMacro/tests/exit.tmp b/Tools/LuaMacro/tests/exit.tmp new file mode 100644 index 0000000..d56d271 --- /dev/null +++ b/Tools/LuaMacro/tests/exit.tmp @@ -0,0 +1,3 @@ +print 'hello'
+os.exit()
+
diff --git a/Tools/LuaMacro/tests/forall1.lua b/Tools/LuaMacro/tests/forall1.lua new file mode 100644 index 0000000..65589e2 --- /dev/null +++ b/Tools/LuaMacro/tests/forall1.lua @@ -0,0 +1,8 @@ +local macro = require 'macro'
+
+macro.define('forall',function(get)
+ local var = get:iden()
+ local t,v = get:next() -- will be 'in'
+ local rest = tostring(get:upto 'do')
+ return ('for _,%s in ipairs(%s) do'):format(var,rest)
+end)
diff --git a/Tools/LuaMacro/tests/lc.lua b/Tools/LuaMacro/tests/lc.lua new file mode 100644 index 0000000..fc8973d --- /dev/null +++ b/Tools/LuaMacro/tests/lc.lua @@ -0,0 +1,297 @@ +-- Simplifying writing C extensions for Lua +-- Adds new module and class constructs; +-- see class1.lc and str.lc for examples. +local M = require 'macro' + +function dollar_subst(s,tbl) + return (s:gsub('%$%((%a+)%)',tbl)) +end + +-- reuse some machinery from the C-skin experiments +local push,pop = table.insert,table.remove +local bstack,btop = {},{} + +local function push_brace_stack (newv) + newv = newv or {} + newv.lev = 0 + push(bstack,btop) + btop = newv +end + +M.define('{',function() + if btop.lev then + btop.lev = btop.lev + 1 + end + return nil,true --> pass-through macro +end) + +M.define('}',function(get,put) + if not btop.lev then + return nil,true + elseif btop.lev == 0 then + local res + if btop.handler then res = btop.handler(get,put) end + if not res then res = put:space() '}' end + btop = pop(bstack) + return res + else + btop.lev = btop.lev - 1 + return nil,true --> pass-through macro + end +end) + +--------- actual implementation begins ------- + +local append = table.insert +local module + +local function register_functions (names,cnames) + local out = {} + for i = 1,#names do + append(out,(' {"%s",l_%s},'):format(names[i],cnames[i])) + end + return table.concat(out,'\n') +end + +local function finalizers (names) + local out = {} + for i = 1,#names do + append(out,names[i].."(L);") + end + return table.concat(out,'\n') +end + +local typedefs + +local preamble = [[ +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> +#ifdef WIN32 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif +]] + +local finis = [[ +static const luaL_reg $(cname)_funs[] = { + $(funs) + {NULL,NULL} +}; + +EXPORT int luaopen_$(cname) (lua_State *L) { + luaL_register (L,"$(name)",$(cname)_funs); + $(finalizers) + return 1; +} +]] + +M.define('module',function(get) + local name = get:string() + local cname = name:gsub('%.','_') + get:expecting '{' + local out = preamble .. typedefs + push_brace_stack{ + name = name, cname = cname, + names = {}, cnames = {}, finalizers = {}, + handler = function() + local out = {} + local funs = register_functions(btop.names,btop.cnames) + local final = finalizers(btop.finalizers) + append(out,dollar_subst(finis, { + cname = cname, + name = name, + funs = funs, + finalizers = final + })) + return table.concat(out,'\n') + end } + module = btop + return out +end) + + +M.define('def',function(get) + local fname = get:iden() + local cname = (btop.ns and btop.ns..'_' or '')..fname + append(btop.names,fname) + append(btop.cnames,cname) + get:expecting '(' + local args = get:list():strip_spaces() + get:expecting '{' + local t,space = get() + indent = space:gsub('^%s*[\n\r]',''):gsub('%s$','') + local out = {"static int l_"..cname.."(lua_State *L) {"} + if btop.massage_arg then + btop.massage_arg(args) + end + for i,arg in ipairs(args) do + local mac = arg[1][2]..'_init' + if arg[3] and arg[3][1] == '=' then + mac = mac .. 'o' + i = i .. ',' .. arg[4][2] + end + append(out,indent..mac..'('..arg[2][2]..','..i..');') + end + append(out,space) + return table.concat(out,'\n') +end) + +M.define('constants',function(get,put) + get:expecting '{' + local consts = get:list '}' :strip_spaces() + --for k,v in pairs(btop) do io.stderr:write(k,'=',tostring(v),'\n') end + -- os.exit() + local fname = 'set_'..btop.cname..'_constants' + local out = { 'static void '..fname..'(lua_State *L) {'} + append(btop.finalizers,fname) + for _,c in ipairs(consts) do + local type,value,name + if #c == 1 then -- a simple int constant: CONST + name = c:pick(1) + type = 'Int' + value = name + else -- Type CONST [ = VALUE ] + type = c:pick(1) + name = c:pick(2) + if #c == 2 then + value = name + else + value = c:pick(4) + end + end + append(out,('%s_set("%s",%s);'):format(type,name,value )) + end + append(out,'}') + return table.concat(out,'\n') +end) + +M.define('assign',function(get) + get:expecting '{' + local asses = get:list '}' :strip_spaces() + local out = {} + for _,c in ipairs(asses) do + append(out,('%s_set("%s",%s);\n'):format(c:pick(1),c:pick(2),c:pick(4)) ) + end + return table.concat(out,'\n') +end) + +typedefs = [[ +typedef const char *Str; +typedef const char *StrNil; +typedef int Int; +typedef double Number; +typedef int Boolean; +]] + + +M.define 'Str_init(var,idx) const char *var = luaL_checklstring(L,idx,NULL)' +M.define 'Str_inito(var,idx,val) const char *var = luaL_optlstring(L,idx,val,NULL)' +M.define 'Str_set(name,value) lua_pushstring(L,value); lua_setfield(L,-2,name)' +M.define 'Str_get(var,name) lua_getfield(L,-1,name); var=lua_tostring(L,-1); lua_pop(L,1)' + +M.define 'StrNil_init(var,idx) const char *var = lua_tostring(L,idx)' + +M.define 'Int_init(var,idx) int var = luaL_checkinteger(L,idx)' +M.define 'Int_inito(var,idx,val) int var = luaL_optinteger(L,idx,val)' +M.define 'Int_set(name,value) lua_pushinteger(L,value); lua_setfield(L,-2,name)' +M.define 'Int_get(var,name) lua_getfield(L,-1,name); var=lua_tointeger(L,-1); lua_pop(L,1)' + +M.define 'Number_init(var,idx) double var = luaL_checknumber(L,idx)' +M.define 'Number_inito(var,idx,val) double var = luaL_optnumber(L,idx,val)' +M.define 'NUmber_set(name,value) lua_pushnumber(L,value); lua_setfield(L,-2,name)' + +M.define 'Boolean_init(var,idx) int var = lua_toboolean(L,idx)' +M.define 'Boolean_set(name,value) lua_pushboolean(L,value); lua_setfield(L,-2,name)' + +M.define 'Value_init(var,idx) int var = idx' + +M.define('lua_tests',function(get) + get:expecting '{' + local body = get:upto '}' + local f = io.open(M.filename..'.lua','w') + f:write(tostring(body)) + f:close() +end) + +------ class support ---------------------- + +local klass_ctor = "static void $(klass)_ctor(lua_State *L, $(klass) *this, $(fargs))" + +local begin_klass = [[ + +typedef struct { + $(fields) +} $(klass); + +define_ $(klass)_init(var,idx) $(klass) *var = $(klass)_arg(L,idx) + +#define $(klass)_MT "$(klass)" + +$(klass) * $(klass)_arg(lua_State *L,int idx) { + $(klass) *this = ($(klass) *)luaL_checkudata(L,idx,$(klass)_MT); + luaL_argcheck(L, this != NULL, idx, "$(klass) expected"); + return this; +} + +$(ctor); + +static void push_new_$(klass)(lua_State *L,$(fargs)) { + $(klass) *this = ($(klass) *)lua_newuserdata(L,sizeof($(klass))); + luaL_getmetatable(L,$(klass)_MT); + lua_setmetatable(L,-2); + $(klass)_ctor(L,this,$(aargs)); +} + +]] + +local end_klass = [[ + +static const struct luaL_reg $(klass)_methods [] = { + $(methods) + {NULL, NULL} /* sentinel */ +}; + +static void $(klass)_register (lua_State *L) { + luaL_newmetatable(L,$(klass)_MT); + luaL_register(L,NULL,$(klass)_methods); + lua_pushvalue(L,-1); + lua_setfield(L,-2,"__index"); + lua_pop(L,1); +} +]] + +M.define('class',function(get) + local name = get:iden() + get:expecting '{' + local fields = get:upto (function(t,v) + return t == 'iden' and v == 'constructor' + end) + get:expecting '(' + local out = {} + local args = get:list() + local f_args = args:strip_spaces() + local a_args = f_args:pick(2) + f_args = table.concat(args:__tostring(),',') + a_args = table.concat(a_args,',') + local subst = {klass=name,fields=tostring(fields),fargs=f_args,aargs=a_args } + local proto = dollar_subst(klass_ctor,subst) + subst.ctor = proto + append(out,dollar_subst(begin_klass,subst)) + append(out,proto) + local pp = {{'iden',name},{'iden','this'}} + push_brace_stack{ + names = {}, cnames = {}, ns = name, cname = name, + massage_arg = function(args) + table.insert(args,1,pp) + end, + handler = function(get,put) + append(module.finalizers,name.."_register") + local methods = register_functions(btop.names,btop.cnames) + return dollar_subst(end_klass,{methods=methods,klass=name,fargs=f_args,aargs=a_args}) + end + } + return table.concat(out,'\n') +end) + diff --git a/Tools/LuaMacro/tests/list.lua b/Tools/LuaMacro/tests/list.lua new file mode 100644 index 0000000..274d482 --- /dev/null +++ b/Tools/LuaMacro/tests/list.lua @@ -0,0 +1,59 @@ +local M = require 'macro'
+List = require 'pl.List'
+
+local list_check
+
+-- list <var-list> [ = <init-list> ]
+-- acts as a 'macro factory', making locally-scoped macros for the variables,
+-- and emitting code to initialize plain variables.
+M.define ('list',function(get)
+ get() -- skip space
+ -- 'list' acts as a 'type' followed by a variable list, which may be
+ -- followed by initial values
+ local values
+ local vars,endt = get:idens (function(t,v)
+ return t == '=' or (t == 'space' and v:find '\n')
+ end)
+ -- there is an initialization list
+ if endt[1] == '=' then
+ values,endt = get:list '\n'
+ else
+ values = {}
+ end
+ -- build up the initialization list
+ for i,name in ipairs(vars) do
+ M.define_scoped(name,list_check)
+ values[i] = 'List('..tostring(values[i] or '')..')'
+ end
+ local lcal = M._interactive and '' or 'local '
+ local res = lcal..table.concat(vars,',')..' = '..table.concat(values,',')..tostring(endt)
+ return res
+end)
+
+function list_check (get,put)
+ local t,v = get:peek(1)
+ if t ~= '[' then return nil, true end -- pass-through; plain var reference
+ get:expecting '['
+ local args = get:list(']',':')
+ -- it's just plain table access
+ if #args == 1 then return '['..tostring(args[1])..']',true end
+
+ -- two items separated by a colon; use sensible defaults
+ M.assert(#args == 2, "slice has two arguments!")
+ local start,finish = tostring(args[1]),tostring(args[2])
+ if start == '' then start = '1' end
+ if finish == '' then finish = '-1' end
+
+ -- look ahead to see if we're on the left hand side of an assignment
+ if get:peek(1) == '=' then
+ get:next() -- skip '='
+ local rest,eoln = get:upto '\n'
+ rest,eoln = tostring(rest),tostring(eoln)
+ return (':slice_assign(%s,%s,%s)%s'):format(start,finish,rest,eoln),true
+ else
+ return (':slice(%s,%s)'):format(start,finish),true
+ end
+end
+
+
+
diff --git a/Tools/LuaMacro/tests/mmath.lua b/Tools/LuaMacro/tests/mmath.lua new file mode 100644 index 0000000..ad813b5 --- /dev/null +++ b/Tools/LuaMacro/tests/mmath.lua @@ -0,0 +1,7 @@ +-- shows how a macro module pulled in with require_ +-- can return a substitution value. In this case, +-- it would be better to use include_, but this +-- method is more general +return function() + return 'local sin,cos = math.sin, math.cos\n' +end diff --git a/Tools/LuaMacro/tests/mod.m.lua b/Tools/LuaMacro/tests/mod.m.lua new file mode 100644 index 0000000..50151e6 --- /dev/null +++ b/Tools/LuaMacro/tests/mod.m.lua @@ -0,0 +1,48 @@ +require_ 'module' + +local function dump(text) + print (text) +end + +function one () + return two() +end + +class Fred + + function _init(self,x) + @set(x or 1) + end + + function set(self,x) + @x = x + end + + function get(self) + return @x + end + + function set2(self) + @set(0) + end + +end + +class Alice : Fred + function __tostring(self) + return "Alice "..tostring(@x) + end + + function set2(self) + @set(1) + end +end + + +function two () + return 42 +end + + + + diff --git a/Tools/LuaMacro/tests/proto.lua b/Tools/LuaMacro/tests/proto.lua new file mode 100644 index 0000000..dff532c --- /dev/null +++ b/Tools/LuaMacro/tests/proto.lua @@ -0,0 +1,31 @@ +local M = require 'macro' + +function _assert_arg(val,idx,t) + if type(val) ~= t then + error(("type mismatch argument %d: got %s, expecting %s"):format(idx,type(val),t),2) + end +end + +M.define('Function',function(get,put) + local name = get:upto '(' + local args,endt = get:list() + args = args:strip_spaces() + local argnames,names = {},{} + for i,a in ipairs(args) do + local name = a:pick(1) + M.assert(a:pick(2) == ':') + table.remove(a,1) + table.remove(a,1) + argnames[i] = {{'iden',name}} + names[i] = name + end + get:expecting ':' + local rtype, endt = get:upto '\n' + put :keyword 'function' :space() :tokens(name) '(' :list(argnames) ')' :space '\n' + put :space() + for i,a in ipairs(args) do + local tp = a:pick(1) + put :iden('_assert_arg') '(' :iden(names[i]) ',' :number(i) ',' :string(tp) ')' ';' + end + return put +end) diff --git a/Tools/LuaMacro/tests/qw.lua b/Tools/LuaMacro/tests/qw.lua new file mode 100644 index 0000000..80fb26d --- /dev/null +++ b/Tools/LuaMacro/tests/qw.lua @@ -0,0 +1,16 @@ +local macro = require 'macro'
+macro.define('qw',function(get,put)
+ local append = table.insert
+ local t,v = get()
+ local res = {{'{','{'}}
+ t,v = get:next()
+ while t ~= ')' do
+ if t ~= ',' then
+ append(res,{'string','"'..v..'"'})
+ append(res,{',',','})
+ end
+ t,v = get:next()
+ end
+ append(res,{'}','}'})
+ return res
+end)
\ No newline at end of file diff --git a/Tools/LuaMacro/tests/rawhash.lua b/Tools/LuaMacro/tests/rawhash.lua new file mode 100644 index 0000000..fdd86b3 --- /dev/null +++ b/Tools/LuaMacro/tests/rawhash.lua @@ -0,0 +1,34 @@ +local M = require 'macro' + +local concat = table.concat + +M.define ('Tab',function(get) + get() -- skip space + -- 'Tab' acts as a 'type' followed by a variable list + local vars,endt = get:idens (function(t,v) + return t == '=' or (t == 'space' and v:find '\n') + end) + local values = {} + for i,name in ipairs(vars) do + M.define_scoped(name,function(get,put) + local t,v = get:peek(1) + if t ~= '[' then return nil, true end -- pass-through; plain var reference + get:expecting '[' + local args = get:list(']','') + local index = args[1] + for i = 1,#index do + if index[i][1] == '#' and (i == #index or index[i+1][1] ~= 'iden') then + table.insert(index,i+1,{'iden',name}) + end + end + return '['..tostring(index)..']',true + end) + values[i] = '{}' + end + local lcal = M._interactive and '' or 'local ' + local res = lcal..concat(vars,',')..' = '..concat(values,',')..tostring(endt) + return res +end) + + + diff --git a/Tools/LuaMacro/tests/readme.md b/Tools/LuaMacro/tests/readme.md new file mode 100644 index 0000000..89c07d7 --- /dev/null +++ b/Tools/LuaMacro/tests/readme.md @@ -0,0 +1,23 @@ +To run the examples, you can either directly use
+
+ $> lua ../luam.lua run-tests.lua
+
+or make a shortcut to luam.lua on your path.
+
+ $> luam run-tests.lua
+
+`run-tests.lua` is both an example of using macros and a little test harness for the package.
+
+The shortcut should look something like this
+
+ @echo off
+ rem luam.bat
+ lua c:\mylua\LuaMacro\luam.lua %*
+
+or this:
+
+ # luam
+ lua /home/frodo/lua/LuaMacro/luam.lua $*
+
+and then should be copied somewhere on your executable path.
+
diff --git a/Tools/LuaMacro/tests/run-tests.lua b/Tools/LuaMacro/tests/run-tests.lua new file mode 100644 index 0000000..8bc3317 --- /dev/null +++ b/Tools/LuaMacro/tests/run-tests.lua @@ -0,0 +1,35 @@ +-- similar syntax to tests.bat, but more portable and aware of errors. +require_ 'forall' +require_ 'qw' +local lua52 = _VERSION:match '5.2' +local lua51 = not lua52 +def_ put io.stderr:write + +local tests = qw(dollar,lambda,try,block,forall,scope,do,const,with,case,mod,test,rawhash) + +local luam = lua51 and 'luam' or 'luam52' + +function run (f) + put(f,': ') + local res = os.execute(luam..' '..f) + if (lua52 and not res) or (lua51 and res ~= 0) then + put 'failed!\n' + os.exit(1) + else + put 'ok\n' + end +end + +forall f in tests do + f = 'test-'..f..'.lua' + run(f) +end + +run '-lcskin test-cskin.lua' + +if pcall(require,'pl') then + run 'test-list.lua' + run 'test-pl-list.lua' +end + + diff --git a/Tools/LuaMacro/tests/str.l.c b/Tools/LuaMacro/tests/str.l.c new file mode 100644 index 0000000..e15891c --- /dev/null +++ b/Tools/LuaMacro/tests/str.l.c @@ -0,0 +1,19 @@ +// preprocess using luam -C -llc -o str.c str.l.c +#include <string.h> + +module "str" { + + def at (Str s, Int i = 0) { + lua_pushlstring(L,&s[i-1],1); + return 1; + } + + def upto (Str s, Str delim = " ") { + lua_pushinteger(L, strcspn(s,delim) + 1); + return 1; + } + +} + + + diff --git a/Tools/LuaMacro/tests/test-assert.lua b/Tools/LuaMacro/tests/test-assert.lua new file mode 100644 index 0000000..fbe5609 --- /dev/null +++ b/Tools/LuaMacro/tests/test-assert.lua @@ -0,0 +1,10 @@ +require_ 'macro.try'
+
+def_ ASSERT(condn,expr) if condn then else error(expr) end
+
+try
+ ASSERT(2 == 1,"damn..".. 2 .." not equal to ".. 1)
+except (e)
+ print('threw:',e)
+end
+
diff --git a/Tools/LuaMacro/tests/test-atm.lua b/Tools/LuaMacro/tests/test-atm.lua new file mode 100644 index 0000000..7832b8d --- /dev/null +++ b/Tools/LuaMacro/tests/test-atm.lua @@ -0,0 +1,13 @@ +@let env = os.getenv +@if env 'P' +print 'P env was set' +@if A +print 'Global A was true' +@end +@elseif A +print 'Global A was true, no P' +@else +print 'Neither P or A' +@end + + diff --git a/Tools/LuaMacro/tests/test-block.lua b/Tools/LuaMacro/tests/test-block.lua new file mode 100644 index 0000000..22c5ae5 --- /dev/null +++ b/Tools/LuaMacro/tests/test-block.lua @@ -0,0 +1,13 @@ +-- in this case, a simple statement macro can be used
+-- `block ... end` expands to `(function() ... end)`
+
+def_ block (function() _END_")"
+
+function peval(fun)
+ print(fun())
+end
+
+peval block
+ return 10,'hello',54
+end
+
diff --git a/Tools/LuaMacro/tests/test-case.lua b/Tools/LuaMacro/tests/test-case.lua new file mode 100644 index 0000000..4f7eb25 --- /dev/null +++ b/Tools/LuaMacro/tests/test-case.lua @@ -0,0 +1,21 @@ +def_ OF_ def_ (of elseif _value ==)
+def_ case(x) do OF_ 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
+
+assert(test(10)==1)
+assert(test(20)==2)
+assert(test(30)==3)
+
+
diff --git a/Tools/LuaMacro/tests/test-const.lua b/Tools/LuaMacro/tests/test-const.lua new file mode 100644 index 0000000..9069568 --- /dev/null +++ b/Tools/LuaMacro/tests/test-const.lua @@ -0,0 +1,10 @@ +require_ 'const'
+do
+ const N,M = 10,20
+ do
+ const N = 5
+ assert(N == 5)
+ end
+ assert(N == 10 and M == 20)
+end
+assert(N == nil and M == nil)
diff --git a/Tools/LuaMacro/tests/test-cskin.lua b/Tools/LuaMacro/tests/test-cskin.lua new file mode 100644 index 0000000..5e6a7c8 --- /dev/null +++ b/Tools/LuaMacro/tests/test-cskin.lua @@ -0,0 +1,47 @@ +-- run like so: luam -lcskin test-cskin.lua
+
+class Named {
+ def _init(self,name) { -- name for ctor
+ self.name = name
+ }
+
+ def __tostring(self) { -- metamethod
+ return self.name
+ }
+}
+
+class Shamed: Named { -- doesn't have to define a ctor
+ def __tostring(self) {
+ return "shame on "..self.name
+ }
+}
+
+class Person : Named {
+ def _init(self,name,age) { -- ctor must call inherited ctor explicitly
+ Named._init(self,name)
+ self:set_age(age)
+ }
+
+ def set_age(self,age) { -- plain method
+ self.age = age;
+ }
+
+ def __tostring(self) {
+ return Named.__tostring(self)..' age '..self.age
+ }
+}
+
+a = Named 'Alice'
+print(a)
+b = Shamed 'Job'
+print(b)
+
+aa = {|Named(n)| n in {'Johan','Peter','Mary'}}
+
+forall(a in aa) { print(a) }
+
+p = Person ('Bob',12)
+print(p)
+
+
+
diff --git a/Tools/LuaMacro/tests/test-do.lua b/Tools/LuaMacro/tests/test-do.lua new file mode 100644 index 0000000..7a8697c --- /dev/null +++ b/Tools/LuaMacro/tests/test-do.lua @@ -0,0 +1,28 @@ +require_ 'macro.do'
+-- unrolling a loop
+y = 0
+do_( i,1, 10,
+ y = y + i
+)
+assert(y == 55)
+
+-- do_ defined a _local_ macro 'i'
+assert(i == nil)
+
+
+-- tuples usually expand to A_1,A_2,A_3 and so forth
+tuple(3) A,B
+B = 10,20,30
+print(B)
+
+def_ do3(v,s) do_(v,1,3,s)
+
+-- but inside a do_ statements with tuples work element-wise
+-- debug_
+do_(k,1,3,
+ A = B/2
+)
+--[[
+print(A)
+--]]
+
diff --git a/Tools/LuaMacro/tests/test-dollar.lua b/Tools/LuaMacro/tests/test-dollar.lua new file mode 100644 index 0000000..71d6a25 --- /dev/null +++ b/Tools/LuaMacro/tests/test-dollar.lua @@ -0,0 +1,7 @@ +require_ 'dollar'
+print($PATH)
+if $(ls) ~= 0 then
+ print($(dir /B)) -- so there!
+end
+
+
diff --git a/Tools/LuaMacro/tests/test-forall.lua b/Tools/LuaMacro/tests/test-forall.lua new file mode 100644 index 0000000..0139e97 --- /dev/null +++ b/Tools/LuaMacro/tests/test-forall.lua @@ -0,0 +1,33 @@ +require_ 'macro.forall'
+
+def_ dump(t) print '---'; forall val in t do print(val) end
+
+forall x in {10,20,30} do print(x) end
+
+t = {'hello','dolly'}
+print '---'
+forall name in t do print(name) end
+print '---'
+forall x in t if x:match 'o$' do print(x) end
+
+-- a wee bit tautological, but valid!
+print '---'
+forall x in L{x^2 | x in {10,20,30}} do print(x) end
+
+t = L{s:upper() | s in {'one','two','three'} if s ~= 'two'}
+
+dump(t)
+
+forall i = 1,5 do print(i) end
+
+t = L{2*i|i=1,10}
+
+dump(t)
+
+-- identity matrix using nested list comprehensions.
+t = L{L{i==j and 1 or 0 | j=1,3} | i=1,3}
+
+-- note the other form of LCs: using 'for' means that you explicitly want
+-- the generic Lua for-statement.
+ls = L{line for line in io.lines 'test-forall.lua'}
+print('length of this file',#ls)
diff --git a/Tools/LuaMacro/tests/test-forall1.lua b/Tools/LuaMacro/tests/test-forall1.lua new file mode 100644 index 0000000..c16d77c --- /dev/null +++ b/Tools/LuaMacro/tests/test-forall1.lua @@ -0,0 +1,4 @@ +require_ 'forall1'
+
+forall i in {10,20,30} do print(i) end
+
diff --git a/Tools/LuaMacro/tests/test-include.lua b/Tools/LuaMacro/tests/test-include.lua new file mode 100644 index 0000000..9bea9ed --- /dev/null +++ b/Tools/LuaMacro/tests/test-include.lua @@ -0,0 +1,2 @@ +include_ 'test.inc'
+print(answer)
diff --git a/Tools/LuaMacro/tests/test-lambda.lua b/Tools/LuaMacro/tests/test-lambda.lua new file mode 100644 index 0000000..649b77d --- /dev/null +++ b/Tools/LuaMacro/tests/test-lambda.lua @@ -0,0 +1,18 @@ +require_ 'macro.lambda'
+ok = pcall(require,'pl')
+if not ok then return print 'test-lambda needs penlight' end
+ls = List{10,20,30}
+assert(ls:map(\x(x+1)) == List{11,21,31})
+assert((\(42))() == 42 )
+
+F = \x,y(x - y)
+
+G = \x(F(x,10))
+
+assert(G(11) == 1)
+
+ls = List { (\(10,20,30))() }
+assert(ls == List{10,20,30})
+
+
+
diff --git a/Tools/LuaMacro/tests/test-list.lua b/Tools/LuaMacro/tests/test-list.lua new file mode 100644 index 0000000..f175ccc --- /dev/null +++ b/Tools/LuaMacro/tests/test-list.lua @@ -0,0 +1,36 @@ +--- sugar for making pl.List work like Python lists. +-- The list macro is a factory which generates macros which 'shadow' the variable +-- and kick in when they are followed by [...]. +require_ 'list' + +-- the two forms of 'list' initialization +-- (altho it grabs values upto '\n', this only happens outside a () or {}, +-- so multi-line initializations are possible +list ls,lo = {10,20,30},{'A','ay', + 'B','C'} +list two + +-- the above statements created both the macros 'ls' and values 'ls', etc. +two:append(1) +two:append(2) + +-- seen as plain table access +print(ls[2]) + +-- special treatment for slice notation +print(ls[1:2]) + +-- if we are on the LHS, then adjust accordingly +ls[1:2] = {11,21,22} + +print(ls[2:]) + +print(ls, two, lo) + +-- like in Python, this makes a copy of all of the list +print(ls[:]) + + + + + diff --git a/Tools/LuaMacro/tests/test-macro.lua b/Tools/LuaMacro/tests/test-macro.lua new file mode 100644 index 0000000..59b569b --- /dev/null +++ b/Tools/LuaMacro/tests/test-macro.lua @@ -0,0 +1,113 @@ +require 'pl'
+local macro = require 'macro'
+
+tmp = [[
+for i = 1,10 do
+ fred("hello",i)
+ f(10,'hello')
+ print(str(233))
+ bonzo a,b,c;
+ fun2(\x(x+1),\(a:get()),\x(\y(x+y)))
+ frederick block
+ for i = 1,10 do
+ alpha block
+ print(i)
+ end
+ end
+ end
+
+ try
+ banzai(dog)
+ except(ex)
+ print(ex)
+ end
+end
+]]
+
+local M = macro
+local define = M.define
+
+define 'fred alice'
+define 'alice print'
+define 'f(x,y) fred(x..y))'
+define ('str(x)',function(x)
+ return '"'..x[1][2]..'"'
+end)
+define ('bonzo',function(get)
+ local t = get(); M.assert(t == 'space','space required')
+ local args = M.get_names(get,';')
+ local res = {}
+ M.put_keyword(res,'local')
+ M.put_names(res,args)
+ return res
+end)
+
+define ('@',function(get)
+ return '"'..os.date('%c')..'"'
+end)
+
+define ('\\',function(get)
+ --local args = M.get_names(get,'(')
+ local args = get:names('(')
+ local body = M.get_list(get)
+ --[[
+ local res = {}
+ M.put_keyword(res,'function')
+ M.put(res,'(')
+ M.put_names(res,args)
+ M.put (res,')')
+ M.put_keyword(res,'return')
+ M.put_list(res,body)
+ M.put_space(res)
+ M.put_keyword(res,'end')
+ return res
+ --]]
+ local put = M.Putter()
+ --print('*****',put,getmetatable(put).keyword)
+ --return put;
+ put:keyword 'function' '(' : names(args) ')'
+ return put:keyword 'return' : list(body) : space() : keyword 'end'
+end)
+
+define ('block',function(get)
+ M.block_handler(0,function(get)
+ return ')'
+ end)
+ local res = {}
+ M.put(res,'(')
+ M.put_keyword(res,'function')
+ M.put(res,'(')
+ M.put(res,')')
+ return res
+end)
+
+
+define ('_END_',function(get)
+ local str = M.get_string(get)
+ M.block_handler(0,function()
+ return str
+ end)
+end)
+
+define 'E_ _END_ " end"'
+
+define 'try do local stat_,r_ = pcall(function()'
+define 'except(e) end); E_ if stat_ then if r_~=nil then return r_ end else local e = r_ '
+
+
+local out = stringio.create()
+macro.substitute(tmp,out)
+print(out:value())
+
+if arg[1] == '-i' then
+
+io.write '? '
+local line = io.read()
+while line do
+ local sub,err = macro.substitute_tostring(line..'\n')
+ if sub then io.write(sub) else io.write(err,'\n') end
+ io.write '? '
+ line = io.read()
+end
+
+end
diff --git a/Tools/LuaMacro/tests/test-mod.lua b/Tools/LuaMacro/tests/test-mod.lua new file mode 100644 index 0000000..2d51cb0 --- /dev/null +++ b/Tools/LuaMacro/tests/test-mod.lua @@ -0,0 +1,20 @@ +local mod = require 'mod' +-- def_ show(expr) print(_STR_(expr),expr) + +assert(mod.one() == 42) + +f = mod.Fred(22) + +assert(f:get() == 22) +f:set2() +assert(f:get() == 0) + +a = mod.Alice() +a:set2() +assert(a:get() == 1) +a:set(66) +assert(tostring(a) == "Alice 66") + + + + diff --git a/Tools/LuaMacro/tests/test-pl-list.lua b/Tools/LuaMacro/tests/test-pl-list.lua new file mode 100644 index 0000000..26641aa --- /dev/null +++ b/Tools/LuaMacro/tests/test-pl-list.lua @@ -0,0 +1,12 @@ +-- Wrapping list comprehensions so that they return a pl.List +-- the trick here is that the L macro will pop its macro stack at the end, +-- if set. Here we want to wrap L{...} so it returns a list, so we have +-- List(L{...}). By pushing ')', L will emit the close parens for us. +require 'pl' +require_ 'macro.forall' + +def_ LL List( _PUSH_('L',')') L + +print(LL{i|i=1,3}..LL{i|i=10,20,5}) + +print( LL{List{k,v} for k,v in pairs{A=1,B=2}} ) diff --git a/Tools/LuaMacro/tests/test-proto.lua b/Tools/LuaMacro/tests/test-proto.lua new file mode 100644 index 0000000..753c9fd --- /dev/null +++ b/Tools/LuaMacro/tests/test-proto.lua @@ -0,0 +1,10 @@ +require_ "proto" + +Function bonzo (a: number, b: string) : string + return a .. b +end + +print (bonzo(10,"hello")) +print (bonzo("hello")) ---> blows up! + + diff --git a/Tools/LuaMacro/tests/test-qw.lua b/Tools/LuaMacro/tests/test-qw.lua new file mode 100644 index 0000000..bc8a47d --- /dev/null +++ b/Tools/LuaMacro/tests/test-qw.lua @@ -0,0 +1,3 @@ +require_ 'qw'
+
+print(qw(one two three))
diff --git a/Tools/LuaMacro/tests/test-rawhash.lua b/Tools/LuaMacro/tests/test-rawhash.lua new file mode 100644 index 0000000..6697cf8 --- /dev/null +++ b/Tools/LuaMacro/tests/test-rawhash.lua @@ -0,0 +1,35 @@ +require_ "rawhash" + +function test () + Tab mytable, another + + t = {1,3} + + -- Here # is short for #mytable + mytable[#+1] = 1 + mytable[#+1] = 2 + + -- without indexing, behaves just like a table reference + assert(type(mytable)=='table') + + -- it is still possible to use #t explicitly + assert(mytable [#]==mytable[#t]) + + assert(mytable[#-1] == mytable[1]) + + for i = 1,10 do another[#+1] = i end + for i = 1,10 do assert(another[i] == i) end + +end + +test() + +-- although mytable is a macro, its scope is limited to test() +assert(mytable == nil) + + + + + + + diff --git a/Tools/LuaMacro/tests/test-require.lua b/Tools/LuaMacro/tests/test-require.lua new file mode 100644 index 0000000..f871cf0 --- /dev/null +++ b/Tools/LuaMacro/tests/test-require.lua @@ -0,0 +1,3 @@ +require_ 'mmath' + +print(sin(1.2) + cos(0.3)) diff --git a/Tools/LuaMacro/tests/test-scope.lua b/Tools/LuaMacro/tests/test-scope.lua new file mode 100644 index 0000000..8007225 --- /dev/null +++ b/Tools/LuaMacro/tests/test-scope.lua @@ -0,0 +1,17 @@ +-- simple macros created using def_ are lexically scoped
+do
+ def_ X 42
+ assert(X == 42)
+ do
+ def_ X 'hello'
+ assert(X == 'hello')
+ do
+ def_ X 999
+ assert (X == 999)
+ end
+ assert(X == 'hello')
+ end
+ assert(X == 42)
+end
+assert (X==nil)
+
diff --git a/Tools/LuaMacro/tests/test-test.lua b/Tools/LuaMacro/tests/test-test.lua new file mode 100644 index 0000000..42f7860 --- /dev/null +++ b/Tools/LuaMacro/tests/test-test.lua @@ -0,0 +1,50 @@ +require_ 'assert' + +-- can compare table values as well +assert_ 2 == 2 +assert_ print == print +assert_ {one=1,two=2} == {two=2,one=1} + +assert_ 'hello' matches '^hell' +assert_ 2 > 1 + +--assert_ 3 < 2 -- quite wrong! + +-- if the first expression returns multiple values, then +-- the second can match this with parentheses + +function two() return 40,2 end + +assert_ two() == (40,2) + +function three() return {1,2},nil,'three' end + +assert_ three() == ({1,2},nil,'three') + +-- 'throws' only succeeds if the expression did actually raise an error, +-- and the error string does match the error message. + +function bad() error 'something bad!' end + +assert_ bad() throws "something bad!" + +a = nil + +assert_ a.x throws "attempt to index global 'a'" + +-- can of course redefine assert_... +def_ assert assert_ + +-- This is an experimental feature, which matches two numbers up to the +-- precision supplied in the second number. This only happens if the test +-- number has a fractional part, and the number must be explicitly +-- be in %f formaat. +assert 3.1412 == 3.14 +assert 2302.24432 == 2302.2 +assert 100 == 100 +assert 1.1e-3 == 0.001 + + + + + diff --git a/Tools/LuaMacro/tests/test-try.lua b/Tools/LuaMacro/tests/test-try.lua new file mode 100644 index 0000000..cfd68a4 --- /dev/null +++ b/Tools/LuaMacro/tests/test-try.lua @@ -0,0 +1,52 @@ +require_ 'macro.try'
+local check
+try
+ try
+ if arg[1] then error('throw!') end
+ except(err)
+ print('shd be throw',err)
+ end
+ a.x = 0
+except(e)
+ check = e
+end
+
+-- the truly tricky case here is the plain return, since we need to distinguish
+-- it from merely leaving the protected block.
+function testtry(n)
+ try
+ if n == 0 then return 'cool'
+ elseif n == 1 then return 'cool',42
+ elseif n == 2 then return --aha
+ elseif n == 3 then
+ error('throw baby!')
+ end
+ except(msg)
+ return nil,msg
+ end
+ return 42,'answer'
+end
+
+function match (x,y)
+ local ok
+ if type(x) == 'string' and type(y) == 'string' then
+ ok = y:find(x)
+ else
+ ok = x == y
+ end
+ if not ok then print('mismatch',x,y) end
+ return ok
+end
+
+function assert2 (x1,x2,y1,y2)
+ assert(match(x1,y1) and match(x2,y2))
+end
+
+assert(match("attempt to index global 'a' %(a nil value%)$",check))
+assert2('cool',nil,testtry(0))
+assert2('cool',42,testtry(1))
+assert2(42,'answer',testtry(2))
+assert2(nil,'throw baby!$',testtry(3))
+assert2(42,'answer',testtry(4))
+
+
diff --git a/Tools/LuaMacro/tests/test-with.lua b/Tools/LuaMacro/tests/test-with.lua new file mode 100644 index 0000000..ac5d0fe --- /dev/null +++ b/Tools/LuaMacro/tests/test-with.lua @@ -0,0 +1,18 @@ +require_ 'macro.with'
+
+aLongTableName = {}
+with aLongTableName do
+ .a = 1
+ .b = {{x=1},{x=2}}
+ .c = {f = 2}
+ print(.a,.c.f,.b[1].x)
+end
+
+def_ @ return
+def_ F function
+
+F f(x) @ x+1 end
+
+print(f(10))
+
+
diff --git a/Tools/LuaMacro/tests/test.inc b/Tools/LuaMacro/tests/test.inc new file mode 100644 index 0000000..4276342 --- /dev/null +++ b/Tools/LuaMacro/tests/test.inc @@ -0,0 +1,3 @@ +print 'hello'
+print 'dolly'
+local answer = 42
diff --git a/Tools/LuaMacro/tests/tests.bat b/Tools/LuaMacro/tests/tests.bat new file mode 100644 index 0000000..f0ff583 --- /dev/null +++ b/Tools/LuaMacro/tests/tests.bat @@ -0,0 +1 @@ +for %%p in (dollar,lambda,try,block,forall,scope,do,const) do luam test-%%p.lua
diff --git a/Tools/LuaMacro/tests/tests.lua b/Tools/LuaMacro/tests/tests.lua new file mode 100644 index 0000000..865c928 --- /dev/null +++ b/Tools/LuaMacro/tests/tests.lua @@ -0,0 +1,27 @@ +require_ 'macro.forall' +require_ 'qw' +local function execute (name) + local file = 'test-'..name..'.lua' + print('executing '..file) + os.execute('luam '..file) +end +forall name in qw(dollar lambda try block scope do const rawhash include test) do + execute (name) +end + +if pcall(require,'pl') then + execute 'list' +end + +local function exec (cmd) + print (cmd) + os.execute(cmd) +end + +exec 'luam -lcskin test-cskin.lua' +exec 'luam test-atm.lua' +exec 'luam -VA test-atm.lua' +exec 'set P=1 && luam test-atm.lua' +exec 'set P=1 && luam -VA test-atm.lua' + + diff --git a/Tools/LuaMacro/tests/winapi.lc b/Tools/LuaMacro/tests/winapi.lc new file mode 100644 index 0000000..723a91c --- /dev/null +++ b/Tools/LuaMacro/tests/winapi.lc @@ -0,0 +1,210 @@ +#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#include <string.h>
+
+#define eq(s1,s2) (strcmp(s1,s2)==0)
+
+#define WBUFF 2048
+#define MAX_SHOW 100
+
+module "winapi" {
+
+class window {
+ HWND hwnd;
+
+ constructor (HWND h) {
+ this->hwnd = h;
+ }
+
+ static lua_State *sL;
+
+ static BOOL enum_callback(HWND hwnd,LPARAM data) {
+ int ref = (int)data;
+ lua_rawgeti(sL,LUA_REGISTRYINDEX,ref);
+ push_new_window(sL,hwnd);
+ lua_call(sL,1,0);
+ return TRUE;
+ }
+
+ def handle() {
+ lua_pushinteger(L,(int)this->hwnd);
+ return 1;
+ }
+
+ static char buff[WBUFF];
+
+ def get_text() {
+ GetWindowText(this->hwnd,buff,sizeof(buff));
+ lua_pushstring(L,buff);
+ return 1;
+ }
+
+ def show(Int flags = SW_SHOW) {
+ ShowWindow(this->hwnd,flags);
+ return 0;
+ }
+
+ def get_position() {
+ RECT rect;
+ GetWindowRect(this->hwnd,&rect);
+ lua_pushinteger(L,rect.left);
+ lua_pushinteger(L,rect.top);
+ return 2;
+ }
+
+ def get_bounds() {
+ RECT rect;
+ GetWindowRect(this->hwnd,&rect);
+ lua_pushinteger(L,rect.right - rect.left);
+ lua_pushinteger(L,rect.bottom - rect.top);
+ return 2;
+ }
+
+ def is_visible() {
+ lua_pushboolean(L,IsWindowVisible(this->hwnd));
+ return 1;
+ }
+
+ def destroy () {
+ DestroyWindow(this->hwnd);
+ return 0;
+ }
+
+ def resize(Int x0, Int y0, Int w, Int h) {
+ MoveWindow(this->hwnd,x0,y0,w,h,TRUE);
+ return 0;
+ }
+
+ def send_message(Int msg, Int wparam, Int lparam) {
+ SendMessage(this->hwnd,msg,wparam,lparam);
+ }
+
+ def enum_children(Value callback) {
+ int ref;
+ sL = L;
+ lua_pushvalue(L,callback);
+ ref = luaL_ref(L,LUA_REGISTRYINDEX);
+ EnumChildWindows(this->hwnd,&enum_callback,ref);
+ luaL_unref(L,LUA_REGISTRYINDEX,ref);
+ return 0;
+ }
+
+ def get_parent() {
+ push_new_window(L,GetParent(this->hwnd));
+ return 1;
+ }
+
+ def get_module_filename() {
+ int sz = GetWindowModuleFileName(this->hwnd,buff,sizeof(buff));
+ buff[sz] = '\0';
+ lua_pushstring(L,buff);
+ return 1;
+ }
+
+ def __tostring() {
+ GetWindowText(this->hwnd,buff,sizeof(buff));
+ if (strlen(buff) > MAX_SHOW) {
+ strcpy(buff+MAX_SHOW,"...");
+ }
+ lua_pushstring(L,buff);
+ return 1;
+ }
+
+ def __eq(window other) {
+ lua_pushboolean(L,this->hwnd == other->hwnd);
+ return 1;
+ }
+
+}
+
+def find_window(Str cname, Str wname) {
+ HWND hwnd;
+ if (eq(cname,"")) {
+ cname = NULL;
+ }
+ hwnd = FindWindow(cname,wname);
+ push_new_window(L,hwnd);
+ return 1;
+}
+
+def active_window() {
+ push_new_window(L, GetActiveWindow());
+ return 1;
+}
+
+def desktop_window() {
+ push_new_window(L, GetDesktopWindow());
+ return 1;
+}
+
+def enum_windows(Value callback) {
+ int ref;
+ sL = L;
+ lua_pushvalue(L,callback);
+ ref = luaL_ref(L,LUA_REGISTRYINDEX);
+ EnumWindows(&enum_callback,ref);
+ luaL_unref(L,LUA_REGISTRYINDEX,ref);
+ return 0;
+}
+
+def tile_windows(window parent, Boolean horiz, Value kids, Value bounds) {
+ RECT rt;
+ HWND *kids_arr;
+ int i,n_kids;
+ LPRECT lpRect = NULL;
+ if (! lua_isnoneornil(L,bounds)) {
+ lua_pushvalue(L,bounds);
+ Int_get(rt.left,"left");
+ Int_get(rt.top,"top");
+ Int_get(rt.right,"right");
+ Int_get(rt.bottom,"bottom");
+ lua_pop(L,1);
+ }
+ n_kids = lua_objlen(L,kids);
+ kids_arr = (HWND *)malloc(sizeof(HWND)*n_kids);
+ for (i = 0; i < n_kids; ++i) {
+ window *w;
+ lua_rawgeti(L,kids,i+1);
+ w = window_arg(L,-1);
+ kids_arr[i] = w->hwnd;
+ }
+ TileWindows(parent->hwnd,horiz ? MDITILE_HORIZONTAL : MDITILE_VERTICAL, lpRect, n_kids, kids_arr);
+ free(kids_arr);
+ return 0;
+}
+
+def shell_exec(StrNil verb, Str file, StrNil parms, StrNil dir, Int show=SW_SHOWNORMAL) {
+ int res = (int)ShellExecute(NULL,verb,file,parms,dir,show);
+ if (res > 32) {
+ lua_pushboolean(L,1);
+ return 1;
+ } else {
+ const char *msg;
+ switch(res) {
+ #define check_err(NAME) case NAME: msg = #NAME; break;
+ check_err(ERROR_FILE_NOT_FOUND);
+ check_err(ERROR_PATH_NOT_FOUND);
+ check_err(ERROR_BAD_FORMAT);
+ check_err(SE_ERR_ACCESSDENIED);
+ check_err(SE_ERR_ASSOCINCOMPLETE);
+ check_err(SE_ERR_DLLNOTFOUND);
+ check_err(SE_ERR_NOASSOC);
+ check_err(SE_ERR_OOM);
+ check_err(SE_ERR_SHARE);
+ default: msg = "unknown error, probably DDE";
+ #undef check_err
+ }
+ lua_pushnil(L);
+ lua_pushstring(L,msg);
+ return 2;
+ }
+}
+
+constants {
+ SW_HIDE,
+ SW_MAXIMIZE,
+ SW_MINIMIZE,
+ SW_SHOWNORMAL
+}
+
+}
|