有没有办法在Lua文件中调用require,并让模块设置调用它的文件的环境?例如,如果我有一个DSL(特定于域的语言)来定义表中定义的函数Root和Sequence,那么我可以在模块中使用类似setfenv(1,ds
我想到的目标是使用这个行为树DSL,使我的定义文件看起来像这样(或尽可能接近它):
require "behaviortrees"
return Root {
Sequence {
Leaf "leafname",
Leaf "leafname"
}
}
无需专门将Root,Sequence和Leaf明确地纳入范围,或者必须限定诸如behaviortrees.Sequence之类的名称.
简而言之,我正在尝试使定义文件尽可能干净,没有任何多余的线条使树的定义混乱.
我可以在模块中使用类似setfenv(1,dslEnv)的东西来允许我访问全局变量等函数吗?你当然可以.您只需要确定要使用的正确堆栈级别,而不是setfenv调用中的1.通常你会使用带有debug.getinfo调用的循环向上走,直到你在堆栈上找到require函数,然后再移动一些直到找到下一个主块(以防万一有人在函数中调用require) .这是你必须与setfenv一起使用的堆栈级别.但我可以建议……
不同的方法
Lua中的require是可插拔的.您可以向package.loaders数组添加一个函数(称为搜索器),require会在尝试加载模块时调用它.假设您的所有DSL文件都有.bt后缀而不是通常的.lua.然后,您将使用正常Lua搜索器的重新实现,以及您要查找.bt文件而不是.lua文件的差异,并且您将在loadfile返回的函数上调用setfenv.像这样的东西:
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 )
如果你把它放在一个模块中并且需要从主程序中获取一次,那么你就可以在.bt文件的某个地方要求你的DSL文件与自定义环境一起放置你的.lua文件.而且你甚至不需要DSL文件中的require(“behaviortrees”).例如.:
文件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)
