有没有办法在 Lua 文件中调用 require
,并让模块设置调用它的文件的环境?例如,如果我有一个 DSL(领域特定语言),它定义了表中定义的函数 Root
和 Sequence
,我可以有类似 setfenv(1 , dslEnv)
在允许我访问那些像全局变量这样的函数的模块中?
我的目标是使用行为树 DSL,使我的定义文件看起来像这样(或尽可能接近它):
require "behaviortrees"
return Root {
Sequence {
Leaf "leafname",
Leaf "leafname"
}
}
无需明确地将 Root
、Sequence
和 Leaf
纳入范围,也无需限定名称,例如 behaviortrees.Sequence
。
简而言之,我试图使定义文件尽可能干净,没有任何无关的行使树定义困惑。
最佳答案
我可以在模块中使用类似setfenv(1, dslEnv)
的东西来访问全局变量等函数吗?
当然可以。您只需找出要使用的正确堆栈级别,而不是 setfenv
调用中的 1
。通常,您会使用带有 debug.getinfo
调用的循环向上遍历堆栈,直到在堆栈上找到 require
函数,然后再移动一些直到找到下一个主要 block (以防万一有人在函数中调用 require
)。这是您必须与 setfenv
一起使用的堆栈级别。但我可以建议...
不同的方法
Lua 中的require
是可插入的。您可以向 package.loaders
数组添加一个函数(称为搜索器),require
将在它尝试加载模块时调用它。假设您所有的 DSL 文件都有一个 .bt
后缀,而不是通常的 .lua
。然后,您将使用普通 Lua 搜索器的重新实现,不同之处在于您将查找 .bt
文件而不是 .lua
文件,并且您将调用setfenv
在 loadfile
编辑的函数 return
上。像这样:
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
文件的地方。而且您甚至不需要 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)
关于lua - 我可以使用 Lua 的 require 来设置调用文件的环境吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45425374/