Runtime Syntax |
|
Example:
-- Define syntax local S = require "runtimesyntax" S["insert (x) .into (t)"] = function(x,t) table.insert(t, x) end S["increment (t) .by (n)"] = function(t,n) for i in ipairs(t) do t[i] = t[i] + n end end S["check (a) '>' (b) '<' (c)"] = function(a,b,c) local ok = a > b and a < c print(string.format("checking %f < %f < %f", b, a, c), "...", ok and "pass" or "fail") end -- using it local t = {} insert (5) .into (t) insert (6) .into (t) increment (t) .by (10) assert(t[1] == 15 and t[2] == 16) check (#t) '>' (1) '<' (3) print 'done' -- This cases "syntax error: missing (c) at @insert.lua:?:25" check (#t) '>' (1) '<'
Implementation:
-- runtimesyntax.lua -- runtime simulatation of syntax extensions in Lua. -- -- WARNING: not well tested. -- -- (c) 2009 David Manura -- Licensed under the same terms as Lua (MIT license). -- Detect incomplete syntax (optional) local function wrap(o, syntax, pos) if syntax:match'^%s*$' then return o end -- http://lua-users.org/wiki/HiddenFeatures local ud = newproxy(true) local mt = getmetatable(ud) mt.__call = type(o) == 'function' and function(_, ...) return o(...) end or nil mt.__index = type(o) == 'table' and getmetatable(o).__index or nil mt.__gc = function() io.stderr:write("syntax error: missing ", syntax, " at ", pos, "\n") end return ud end local function clear(o) if type(o) == 'userdata' then getmetatable(o).__gc = nil end end -- Build function for rest of syntax. -- syntax - syntax string -- vars - arguments passed to function -- (with n count field to support nils) -- f - function to call after building, passing -- unpacked vars -- oprev - previous function or userdata this syntax -- is appended to -- pos - string indicating position in source local function make(syntax, vars, f, oprev, pos) local var, moresyntax = syntax:match("^%s*%(%s*(%w+)%s*%)(.*)") if var then local o o = wrap(function(vara) clear(oprev) vars[vars.n+1] = vara vars.n = vars.n + 1 return make(moresyntax, vars, f, o, pos) end, moresyntax, pos) return o end local prep, moresyntax = syntax:match("^%s*%.(%w+)%s*(.*)") if prep then local o local mt = {} function mt:__index(prepa) clear(oprev) if prep ~= prepa then error(string.format("syntax error: expecting (%s) got (%s)", prep, tostring(prepa))) end return make(moresyntax, vars, f, o, pos) end o = wrap(setmetatable({}, mt), moresyntax, pos) return o end local prep, moresyntax = syntax:match("^%s*'([^']+)'(.*)") if prep then local o o = wrap(function(prepa) clear(oprev) if prep ~= prepa then error(string.format("syntax error: expecting (%s) got (%s)", prep, tostring(prepa))) end return make(moresyntax, vars, f, o, pos) end, moresyntax, pos) return o end if syntax:match'^%s*$' then return f(unpack(vars, 1, vars.n)), true end error("unrecognized:" .. syntax) end local M = {} local M_mt = {} function M_mt:__newindex(syntax, f) local w, moresyntax = syntax:match("^%s*(%w+)(.*)") if w then _G[w] = function(...) local info = debug.getinfo(2) local pos = (info.source or '?') .. ':' .. (info.name or '?') .. ':' .. (info.currentline or '?') return make(moresyntax, {n=0}, f, nil, pos)(...) end end end setmetatable(M, M_mt) return M