我可以setfenv(1, dslEnv)
在模块中有类似的东西允许我访问像全局变量这样的函数吗?
你当然可以。您只需要找出要使用的正确堆栈级别,而不是1
在您的setfenv
调用中使用。通常,您会使用带有debug.getinfo
调用的循环沿堆栈向上走,直到在堆栈上找到require
函数,然后再移动一些直到找到下一个主要块(以防万一有人调用require
函数)。这是您必须使用的堆栈级别setfenv
。但我可以建议一个...
不同的方法
require
在 Lua 中是可插拔的。package.loaders
您可以向数组添加一个函数(称为搜索器) ,并require
在它尝试加载模块时调用它。假设您所有的 DSL 文件都有一个.bt
后缀,而不是通常的.lua
. 然后,您将使用普通 Lua 搜索器的重新实现,不同之处在于您将查找.bt
文件而不是.lua
文件,并且您将setfenv
调用return
由loadfile
. 像这样的东西:
local function Root( x ) return x end
local function Sequence( x ) return x end
local function Leaf( x ) return x end
local delim = package.config:match( "^(.-)\n" ):gsub( "%%", "%%%%" )
local function searchpath( name, path )
local pname = name:gsub( "%.", delim ):gsub( "%%", "%%%%" )
local msg = {}
for subpath in path:gmatch( "[^;]+" ) do
local fpath = subpath:gsub( "%?", pname ):gsub("%.lua$", ".bt") -- replace suffix
local f = io.open( fpath, "r" )
if f then
f:close()
return fpath
end
msg[ #msg+1 ] = "\n\tno file '"..fpath.."'"
end
return nil, table.concat( msg )
end
local function bt_searcher( modname )
assert( type( modname ) == "string" )
local filename, msg = searchpath( modname, package.path )
if not filename then
return msg
end
local env = { -- create custom environment
Root = Root,
Sequence = Sequence,
Leaf = Leaf,
}
local mod, msg = loadfile( filename )
if not mod then
error( "error loading module '"..modname.."' from file '"..filename..
"':\n\t"..msg, 0 )
end
setfenv( mod, env ) -- set custom environment
return mod, filename
end
table.insert( package.loaders, bt_searcher )
如果你把它放在一个模块中并且require
它从你的主程序中一次,你可以然后require
你的 DSL 文件与自定义环境从.bt
文件中你也可以放置你的.lua
文件的地方。而且您甚至不需要require("behaviortrees")
DSL 文件中的 。例如:
文件xxx.bt
:
return Root {
Sequence {
Leaf "leafname",
Leaf "leafname"
}
}
文件main.lua
:
#!/usr/bin/lua5.1
require( "behaviortrees" ) -- loads the Lua module above and adds to package.loaders
print( require( "xxx" ) ) -- loads xxx.bt (but an xxx Lua module would still take precedence)