summaryrefslogtreecommitdiff
path: root/Tools/LuaMacro/tests
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/LuaMacro/tests')
-rw-r--r--Tools/LuaMacro/tests/cexport.lua77
-rw-r--r--Tools/LuaMacro/tests/class1.lc40
-rw-r--r--Tools/LuaMacro/tests/const.lua9
-rw-r--r--Tools/LuaMacro/tests/cskin.lua127
-rw-r--r--Tools/LuaMacro/tests/dll.c37
-rw-r--r--Tools/LuaMacro/tests/dollar.lua22
-rw-r--r--Tools/LuaMacro/tests/exit.tmp3
-rw-r--r--Tools/LuaMacro/tests/forall1.lua8
-rw-r--r--Tools/LuaMacro/tests/lc.lua297
-rw-r--r--Tools/LuaMacro/tests/list.lua59
-rw-r--r--Tools/LuaMacro/tests/mmath.lua7
-rw-r--r--Tools/LuaMacro/tests/mod.m.lua48
-rw-r--r--Tools/LuaMacro/tests/proto.lua31
-rw-r--r--Tools/LuaMacro/tests/qw.lua16
-rw-r--r--Tools/LuaMacro/tests/rawhash.lua34
-rw-r--r--Tools/LuaMacro/tests/readme.md23
-rw-r--r--Tools/LuaMacro/tests/run-tests.lua35
-rw-r--r--Tools/LuaMacro/tests/str.l.c19
-rw-r--r--Tools/LuaMacro/tests/test-assert.lua10
-rw-r--r--Tools/LuaMacro/tests/test-atm.lua13
-rw-r--r--Tools/LuaMacro/tests/test-block.lua13
-rw-r--r--Tools/LuaMacro/tests/test-case.lua21
-rw-r--r--Tools/LuaMacro/tests/test-const.lua10
-rw-r--r--Tools/LuaMacro/tests/test-cskin.lua47
-rw-r--r--Tools/LuaMacro/tests/test-do.lua28
-rw-r--r--Tools/LuaMacro/tests/test-dollar.lua7
-rw-r--r--Tools/LuaMacro/tests/test-forall.lua33
-rw-r--r--Tools/LuaMacro/tests/test-forall1.lua4
-rw-r--r--Tools/LuaMacro/tests/test-include.lua2
-rw-r--r--Tools/LuaMacro/tests/test-lambda.lua18
-rw-r--r--Tools/LuaMacro/tests/test-list.lua36
-rw-r--r--Tools/LuaMacro/tests/test-macro.lua113
-rw-r--r--Tools/LuaMacro/tests/test-mod.lua20
-rw-r--r--Tools/LuaMacro/tests/test-pl-list.lua12
-rw-r--r--Tools/LuaMacro/tests/test-proto.lua10
-rw-r--r--Tools/LuaMacro/tests/test-qw.lua3
-rw-r--r--Tools/LuaMacro/tests/test-rawhash.lua35
-rw-r--r--Tools/LuaMacro/tests/test-require.lua3
-rw-r--r--Tools/LuaMacro/tests/test-scope.lua17
-rw-r--r--Tools/LuaMacro/tests/test-test.lua50
-rw-r--r--Tools/LuaMacro/tests/test-try.lua52
-rw-r--r--Tools/LuaMacro/tests/test-with.lua18
-rw-r--r--Tools/LuaMacro/tests/test.inc3
-rw-r--r--Tools/LuaMacro/tests/tests.bat1
-rw-r--r--Tools/LuaMacro/tests/tests.lua27
-rw-r--r--Tools/LuaMacro/tests/winapi.lc210
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
+}
+
+}