lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I came up with this string interpolator.

For example if I have some environment table (naah, nothing do with lua environments)


local env = {
   ver1 = "-alpha",
   ver2 = "-beta",
   ver = 1,
   name = "cairo${ver${ver}}", -- cairo${ver1} -> cairo-alpha
   one = {
      two = "${ENV.HOME}" -- /Users/<user> (or /home/<user>)
   },
   depo = "${one.two}/${name}" -- /Users/<user>/cairo-alpha
}

and want to expand env.depo, it would give me

> print( interpolate( env.depo, env ) )
/Users/malkia/cairo-alpha

I haven't check what's out there yet, as it was fun to discover what I can do, later would recheck (and probably laugh at my lame code)

In any case, here is the code, same license as lua/luajit:



local env = {
   ver1 = "-alpha",
   ver2 = "-beta",
   ver = 1,
   name = "cairo${ver${ver}}", -- cairo${ver1} -> cairo-alpha
   one = {
      two = "${ENV.HOME}" -- /Users/<user> (or /home/<user>)
   },
   depo = "${one.two}/${name}" -- /Users/<user>/cairo-alpha
}

local function split(s, d)
   local r, b, e, d = {}, 1, 2, d or '.'
   while e ~= 0 do
      e = s:find(d, b, true) or 0
      b, r[#r+1] = e + 1, s:sub(b, e - 1)
   end
   return r
end

local function interpolate(a, env)
   local rcnt, more, env = 50, true, env or {}
   local function subst(s)
      local skey = s:sub(2, -2)
      local key = interpolate(skey, env)
      local val = env

      -- Handle one.two.three case
      for _, k in ipairs(split(key)) do
	 if val[k] == nil then
	    -- Handle array indices
	    k = tonumber(k)
	    if val[k] == nil then
	       if skey == key then
		  error( 'No value for key [' .. tostring(key) .. ']' )
	       else
error( 'No value for key [' .. tostring(key) .. '] <- [' .. tostring(skey) .. ']' )
	       end
	    end
	 end
	 val = val[k]
      end

      -- Don't expand more than 50 deep
      rcnt = rcnt - 1
      if rcnt == 0 then
	 error( 'Expansion too deep' )
      end

      more = true
      return tostring(val)
   end
   while more do
      more = false
      a = string.gsub(a, "$(%b{})", subst)
   end
   return a
end

-- The Process Environment wrapper
local procenv = setmetatable(
   {}, {
      __index = function(t, k) return os.getenv(k) end,
__newindex = function(...) error("Can't modify process environment") end
   }
)

-- Attach the process environment ot our test environment
-- under the key "ENV". If the key has been re-used, then
-- it's no longer accessible.
local env = setmetatable(
   env, {
      __index = function(t, k)
		   local kenv = "ENV"
		   if k == kenv and rawget(t, kenv)==nil then
		      return procenv
		   end
		end
   }
)

print( interpolate( env.depo, env ) )