当前位置 : 主页 > 网络编程 > lua >

我可以使用Lua的require来设置调用文件的环境吗?

来源:互联网 收集:自由互联 发布时间:2021-06-23
有没有办法在Lua文件中调用require,并让模块设置调用它的文件的环境?例如,如果我有一个DSL(特定于域的语言)来定义表中定义的函数Root和Sequence,那么我可以在模块中使用类似setfenv(1,ds
有没有办法在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函数,然后再移动一些直到找到下一个主块(以防万一有人在函数中调用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)
网友评论