packaging for embedded interpreter

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

packaging for embedded interpreter

edwi
OK, I read the stuff people suggested on "sandbox" and environments, and 
I think I ALMOST understand it, but I've still got a couple of questions.

I've got a c program that embeds a lua interpreter that looks basically 
like this in the critical part:

	lua_dofile(L, "script1.lua");
	lua_dofile(L, "script2.lua");
	while (running)
	{
		//retrieve function, which has to be packaged for script1
		lua_pushstring(L, "doSomething");
		lua_gettable (L, LUA_GLOBALSINDEX);
		//push argument
		lua_pushnumber(L, 23);
		//call function
		lua_call(L,1,1);
		//retrieve return value
		lua_Number num = lua_tonumber(L, -1);
		//AND NOW I DO IT AGAIN FOR "doSomething" packaged for script2
	}

and the lua scripts (both of them) essentially look like this:  

	somevar = 1

	function doSomething(num)
		mynum=mutate (num)
		return mynum
	end
	
	function mutate (innum)
		--dosomething with innum and return a result
	end

What I need is some way to create namespaces for the stuff in each lua 
script that is loaded in with lua_dofile so that C can lua_call 
script1.doSomething and it will correctly call script1.mutate and use
the correct local variables and such.  

If I understand the "Programming in Lua" book in section 15.4, the 
solution is to create a custom environment for each of the chunks
read in by lua_dofile, using this little bit:

	local P={} --create an empty list as the environment
	package = P --??
	local _G = _G --make a local copy of the global environment in case I need 
                      --anything from it
	setfenv(1,P) --set environment of this "function"

Right?  A couple of questions, though.

1) how is "local P={}; package=P" different from "local package = {}"?
2) WHERE do I put the packaging bit in the script files?  At the top, 
   ahead of the functions that get lua_called, or in the one that gets 
   lua_called?  If it goes in the function, any variables global to
   the script (like somevar) will in fact be global to L, right?
3) do I understand correctly that when I create an initial empty 
   environment I basically can't get to anything from the standard
   libraries and such, and therefore pretty much HAVE TO make
   local copies of everything I need before I setfenv?

David


Reply | Threaded
Open this post in threaded view
|

Re: packaging for embedded interpreter

Adrian Sietsma
[hidden email] wrote:
OK, I read the stuff people suggested on "sandbox" and environments, and I think I ALMOST understand it, but I've still got a couple of questions.

I've got a c program that embeds a lua interpreter that looks basically like this in the critical part:

	lua_dofile(L, "script1.lua");
	lua_dofile(L, "script2.lua");
	while (running)
	{
		//retrieve function, which has to be packaged for script1
		lua_pushstring(L, "doSomething");
		lua_gettable (L, LUA_GLOBALSINDEX);
		//push argument
		lua_pushnumber(L, 23);
		//call function
		lua_call(L,1,1);
		//retrieve return value
		lua_Number num = lua_tonumber(L, -1);
		//AND NOW I DO IT AGAIN FOR "doSomething" packaged for script2
	}

and the lua scripts (both of them) essentially look like this:
	somevar = 1

	function doSomething(num)
		mynum=mutate (num)
		return mynum
	end
	
	function mutate (innum)
		--dosomething with innum and return a result
	end


if there is only one return value (a function) in each script, you could make each script return a function with local upvalues :

<at the top>
local somevar, doSomething, mutate

<at the bottom>
return doSomething

<in the c>

int s1, s2, tos,tos1; 		// these could be -ve stack offsets,
		 		// but i prefer variables
tos = lual_gettop(L); 		// top of stack for restore
				// in c++, const int tos = lua_gettop(L)
lua_dofile(L,"script1.lua");	// function script1.doSomething at tos
s1 = lual_gettop(L); 		// script1 function index
lua_dofile(L,"script2.lua");	// function script2.doSomething at tos
tos1 = s2 = lual_gettop(L); 	// script2 function index

while (running)
{
	lua_pushvalue(s1);	//retrieve function for script1
	lua_pushnumber(L, 23);	//push argument
	lua_call(L,1,1);	//call function
	lua_Number num = lua_tonumber(L, -1);//retrieve return value
	// use num ??
	lua_settop(L,tos1); // tidy stack

	//AND NOW DO IT AGAIN FOR script2 "doSomething"
	lua_pushvalue(s2);	//retrieve function for script2
	lua_pushnumber(L, 23);	//push argument
	lua_call(L,1,1);	//call function
	lua_Number num = lua_tonumber(L, -1);//retrieve return value
	// use num ??
	lua_settop(L,tos1); // tidy stack
}
lua_settop(L,tos); // tidy stack

if you have lots of functions, return a table :
<at the top>
local somevar, doSomething, mutate

<at the bottom>
return {do = doSomething, mutate = mutate }

<in the c>
load scripts as above : return is now table
lookup script func as you were, but from local table rather than globals (as below).


however, your question was :
What I need is some way to create namespaces for the stuff in each lua script that is loaded in with lua_dofile so that C can lua_call script1.doSomething and it will correctly call script1.mutate and use the correct local variables and such. If I understand the "Programming in Lua" book in section 15.4, the solution is to create a custom environment for each of the chunks
read in by lua_dofile
yep. it can be done in c

int mt, s1, s2, tos,tos1; 	// these could be -ve stack offsets,
		 		// but i prefer variables
tos = lua_gettop(L);
lua_newtable(L); 		// metatable
mt = lua_gettop(L);
lua_pushstring(L,"__index")
lua_lua_gettable (L, LUA_GLOBALSINDEX)
lua_settable(L,-1); 		// metetable __index = global environ.

lua_newtable(L); 		//env. for s1
s1 = lua_gettop(L);
lua_pushvalue(L,mt);
lua_setmetatable(L, -2);	// set meta to globals

lua_newtable(L); 		//env. for s2
s2 = lua_gettop(L);
lua_pushvalue(L,mt);
lua_setmetatable(L, -2);	// set meta to globals

if (luaL_loadfile(L, "script1.lua") != 0)
	lua_error(L);			
lua_pushvalue(L,s1);
lua_setfenv(L,-2);	// set env of script1 chunk
lua_call(L, 0, LUA_MULTRET, 0) != 0)  // execute script

if (luaL_loadfile(L, "script2.lua") != 0)
	lua_error(L);			
lua_pushvalue(L,s2);
lua_setfenv(L,-2);	// set env of script1 chunk
lua_call(L, 0, LUA_MULTRET, 0);  // execute script
tos1 = lua_gettop(L);

while (running)
{
	lua_pushstring(L, "doSomething");
	lua_gettable (L, s1);	//lookup func in s1 environment.
	lua_pushnumber(L, 23);	//push argument
	lua_call(L,1,1);	//call function
	lua_Number num = lua_tonumber(L, -1);//retrieve return value
	// use num ??
	lua_settop(L,tos1); // tidy stack

	//AND NOW DO IT AGAIN FOR script2 "doSomething"
	lua_pushstring(L, "doSomething");
	lua_gettable (L, s2);	// lookup func in s2
	lua_pushnumber(L, 23);	//push argument
	lua_call(L,1,1);	//call function
	lua_Number num = lua_tonumber(L, -1);//retrieve return value
	// use num ??
	lua_settop(L,tos1); // tidy stack
}
lua_settop(L,tos); // tidy stack

or in lua
, using this little bit:

	local P={} --create an empty list as the environment
	package = P --??
local _G = _G --make a local copy of the global environment in case I need --anything from it
	setfenv(1,P) --set environment of this "function"


1) how is "local P={}; package=P" different from "local package = {}"?
the first gives local P, global package. the second gives local package.

2) WHERE do I put the packaging bit in the script files? At the top, ahead of the functions that get lua_called, or in the one that gets lua_called? If it goes in the function, any variables global to
   the script (like somevar) will in fact be global to L, right?

3) do I understand correctly that when I create an initial empty environment I basically can't get to anything from the standard
   libraries and such, and therefore pretty much HAVE TO make
   local copies of everything I need before I setfenv?
you can set a metatable in the local environment pointing to the global one, if your scripts need to see everything.

setmetatable(t,{__index=_G})


Adrian
ps this is _not_ tested code, just a mish-mash of cut-and-paste bits.