Lua: pass context to loadstring?

I am trying to pass a context into a dynamic expression, which I evaluate each iteration of the for loop. I understand that the download line is only evaluated in a global context, which means that local variables are not available. In my case, I exchange this restriction by converting local to global for string evaluation purposes. Here is what I have:

require 'cosmo' model = { { player = "Cliff", age = 35, gender = "male" }, { player = "Ally", age = 36, gender = "female" }, { player = "Jasmine", age = 13, gender = "female" }, { player = "Lauren", age = 6.5, gender = "female" } } values = { eval = function(args) output = '' condition = assert(loadstring('return ' .. args.condition)) for _, it in ipairs(model) do each = it if condition() then output = output .. each.player .. ' age: ' .. each.age .. ' ' .. '\n' end end return output end } template = "$eval{ condition = 'each.age < 30' }" result = cosmo.fill(template, values) print (result) 

My ultimate goal (besides mastering Lua) is to build XSLT as a tempting engine where I could do something like:

 apply_templates{ match = each.age > 30}[[<parent-player>$each.player</parent-player>]] apply_templates{ match = each.age > 30}[[<child-player>$each.player</child-player>]] 

... and generate different outputs. I am currently stuck on my above hawkish means of sharing a local context through a global one. Does anyone here better understand how I will do what I'm trying to do?

+7
source share
2 answers

You can change the context of a function using setfenv() . This allows you to basically load the loaded function into your own environment. Something like the following should work:

 local context = {} local condition = assert(loadstring('return ' .. args.condition)) setfenv(condition, context) for _, it in ipairs(model) do context['each'] = it if condition() then -- ... 

It will also prevent access to data conditions to access any data that you do not want, or, more important, to change any data that you do not want. Note, however, that you will need to identify any top-level bindings in the context table that you want condition access to (for example, if you want it to have access to the math package, then you will need to insert this into context ). Alternatively, if you don't have a problem with a condition that has global access, and you just want to deal with not making your local global, you can use metatable on context to pass unknown indexes to _G :

 setmetatable(context, { __index = _G }) 
+7
source

It is worth noting that setfenv been removed from Lua 5.2 and loadstring deprecated . 5.2 is quite new, so you don’t have to worry about it for a while, but you can write a boot mode that works for both versions:

 local function load_code(code, environment) if setfenv and loadstring then local f = assert(loadstring(code)) setfenv(f,environment) return f else return assert(load(code, nil,"t",environment)) end end local context = {} context.string = string context.table = table -- etc. add libraries/functions that are safe for your application. -- see: http://lua-users.org/wiki/SandBoxes local condition = load_code("return " .. args.condition, context) 

Version 5.2 load processes as the old loadstring and sets the environment (context, in your example). Version 5.2 also changes the concept of the environment , so loadstring may be the least of your worries. However, this is something that you might think to save yourself some work on the road.

+9
source

All Articles