From 27d6efb5f5a076f825fe2da1875e0cabaf02b4e7 Mon Sep 17 00:00:00 2001 From: chai Date: Wed, 17 Nov 2021 23:03:07 +0800 Subject: + LuaMacro --- Tools/LuaMacro/tests/cexport.lua | 77 +++++++++ Tools/LuaMacro/tests/class1.lc | 40 +++++ Tools/LuaMacro/tests/const.lua | 9 ++ Tools/LuaMacro/tests/cskin.lua | 127 +++++++++++++++ Tools/LuaMacro/tests/dll.c | 37 +++++ Tools/LuaMacro/tests/dollar.lua | 22 +++ Tools/LuaMacro/tests/exit.tmp | 3 + Tools/LuaMacro/tests/forall1.lua | 8 + Tools/LuaMacro/tests/lc.lua | 297 ++++++++++++++++++++++++++++++++++ Tools/LuaMacro/tests/list.lua | 59 +++++++ Tools/LuaMacro/tests/mmath.lua | 7 + Tools/LuaMacro/tests/mod.m.lua | 48 ++++++ Tools/LuaMacro/tests/proto.lua | 31 ++++ Tools/LuaMacro/tests/qw.lua | 16 ++ Tools/LuaMacro/tests/rawhash.lua | 34 ++++ Tools/LuaMacro/tests/readme.md | 23 +++ Tools/LuaMacro/tests/run-tests.lua | 35 ++++ Tools/LuaMacro/tests/str.l.c | 19 +++ Tools/LuaMacro/tests/test-assert.lua | 10 ++ Tools/LuaMacro/tests/test-atm.lua | 13 ++ Tools/LuaMacro/tests/test-block.lua | 13 ++ Tools/LuaMacro/tests/test-case.lua | 21 +++ Tools/LuaMacro/tests/test-const.lua | 10 ++ Tools/LuaMacro/tests/test-cskin.lua | 47 ++++++ Tools/LuaMacro/tests/test-do.lua | 28 ++++ Tools/LuaMacro/tests/test-dollar.lua | 7 + Tools/LuaMacro/tests/test-forall.lua | 33 ++++ Tools/LuaMacro/tests/test-forall1.lua | 4 + Tools/LuaMacro/tests/test-include.lua | 2 + Tools/LuaMacro/tests/test-lambda.lua | 18 +++ Tools/LuaMacro/tests/test-list.lua | 36 +++++ Tools/LuaMacro/tests/test-macro.lua | 113 +++++++++++++ Tools/LuaMacro/tests/test-mod.lua | 20 +++ Tools/LuaMacro/tests/test-pl-list.lua | 12 ++ Tools/LuaMacro/tests/test-proto.lua | 10 ++ Tools/LuaMacro/tests/test-qw.lua | 3 + Tools/LuaMacro/tests/test-rawhash.lua | 35 ++++ Tools/LuaMacro/tests/test-require.lua | 3 + Tools/LuaMacro/tests/test-scope.lua | 17 ++ Tools/LuaMacro/tests/test-test.lua | 50 ++++++ Tools/LuaMacro/tests/test-try.lua | 52 ++++++ Tools/LuaMacro/tests/test-with.lua | 18 +++ Tools/LuaMacro/tests/test.inc | 3 + Tools/LuaMacro/tests/tests.bat | 1 + Tools/LuaMacro/tests/tests.lua | 27 ++++ Tools/LuaMacro/tests/winapi.lc | 210 ++++++++++++++++++++++++ 46 files changed, 1708 insertions(+) create mode 100644 Tools/LuaMacro/tests/cexport.lua create mode 100644 Tools/LuaMacro/tests/class1.lc create mode 100644 Tools/LuaMacro/tests/const.lua create mode 100644 Tools/LuaMacro/tests/cskin.lua create mode 100644 Tools/LuaMacro/tests/dll.c create mode 100644 Tools/LuaMacro/tests/dollar.lua create mode 100644 Tools/LuaMacro/tests/exit.tmp create mode 100644 Tools/LuaMacro/tests/forall1.lua create mode 100644 Tools/LuaMacro/tests/lc.lua create mode 100644 Tools/LuaMacro/tests/list.lua create mode 100644 Tools/LuaMacro/tests/mmath.lua create mode 100644 Tools/LuaMacro/tests/mod.m.lua create mode 100644 Tools/LuaMacro/tests/proto.lua create mode 100644 Tools/LuaMacro/tests/qw.lua create mode 100644 Tools/LuaMacro/tests/rawhash.lua create mode 100644 Tools/LuaMacro/tests/readme.md create mode 100644 Tools/LuaMacro/tests/run-tests.lua create mode 100644 Tools/LuaMacro/tests/str.l.c create mode 100644 Tools/LuaMacro/tests/test-assert.lua create mode 100644 Tools/LuaMacro/tests/test-atm.lua create mode 100644 Tools/LuaMacro/tests/test-block.lua create mode 100644 Tools/LuaMacro/tests/test-case.lua create mode 100644 Tools/LuaMacro/tests/test-const.lua create mode 100644 Tools/LuaMacro/tests/test-cskin.lua create mode 100644 Tools/LuaMacro/tests/test-do.lua create mode 100644 Tools/LuaMacro/tests/test-dollar.lua create mode 100644 Tools/LuaMacro/tests/test-forall.lua create mode 100644 Tools/LuaMacro/tests/test-forall1.lua create mode 100644 Tools/LuaMacro/tests/test-include.lua create mode 100644 Tools/LuaMacro/tests/test-lambda.lua create mode 100644 Tools/LuaMacro/tests/test-list.lua create mode 100644 Tools/LuaMacro/tests/test-macro.lua create mode 100644 Tools/LuaMacro/tests/test-mod.lua create mode 100644 Tools/LuaMacro/tests/test-pl-list.lua create mode 100644 Tools/LuaMacro/tests/test-proto.lua create mode 100644 Tools/LuaMacro/tests/test-qw.lua create mode 100644 Tools/LuaMacro/tests/test-rawhash.lua create mode 100644 Tools/LuaMacro/tests/test-require.lua create mode 100644 Tools/LuaMacro/tests/test-scope.lua create mode 100644 Tools/LuaMacro/tests/test-test.lua create mode 100644 Tools/LuaMacro/tests/test-try.lua create mode 100644 Tools/LuaMacro/tests/test-with.lua create mode 100644 Tools/LuaMacro/tests/test.inc create mode 100644 Tools/LuaMacro/tests/tests.bat create mode 100644 Tools/LuaMacro/tests/tests.lua create mode 100644 Tools/LuaMacro/tests/winapi.lc (limited to 'Tools/LuaMacro/tests') 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 +#include +#include +#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 [ = ] +-- 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 + +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 +#include + +#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 +} + +} -- cgit v1.1-26-g67d0