Runtime Syntax

lua-users home
wiki

This utility module automates the run-time simulation of what resembles syntax extensions. It's somewhat of a hack, intended mostly as a proof of concept, and not well tested. For a truer implementation of this type of thing, see MetaLua.

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

--DavidManura

See Also


RecentChanges · preferences
edit · history
Last edited May 2, 2009 1:35 am GMT (diff)