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

标签 lua lua-5.1 behavior-tree

有没有办法在 Lua 文件中调用 require,并让模块设置调用它的文件的环境?例如,如果我有一个 DSL(领域特定语言),它定义了表中定义的函数 RootSequence,我可以有类似 setfenv(1 , dslEnv) 在允许我访问那些像全局变量这样的函数的模块中?

我的目标是使用行为树 DSL,使我的定义文件看起来像这样(或尽可能接近它):

require "behaviortrees"

return Root {
    Sequence {
        Leaf "leafname",
        Leaf "leafname"
    }
}

无需明确地将 RootSequenceLeaf 纳入范围,也无需限定名称,例如 behaviortrees.Sequence

简而言之,我试图使定义文件尽可能干净,没有任何无关的行使树定义困惑。

最佳答案

我可以在模块中使用类似setfenv(1, dslEnv) 的东西来访问全局变量等函数吗?

当然可以。您只需找出要使用的正确堆栈级别,而不是 setfenv 调用中的 1。通常,您会使用带有 debug.getinfo 调用的循环向上遍历堆栈,直到在堆栈上找到 require 函数,然后再移动一些直到找到下一个主要 block (以防万一有人在函数中调用 require)。这是您必须与 setfenv 一起使用的堆栈级别。但我可以建议...

不同的方法

Lua 中的

require 是可插入的。您可以向 package.loaders 数组添加一个函数(称为搜索器),require 将在它尝试加载模块时调用它。假设您所有的 DSL 文件都有一个 .bt 后缀,而不是通常的 .lua。然后,您将使用普通 Lua 搜索器的重新实现,不同之处在于您将查找 .bt 文件而不是 .lua 文件,并且您将调用setfenvloadfile 编辑的函数 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/

相关文章:

regex - 使用正则表达式检测句子末尾的单词

lua - 代码块的脚本语言

nginx - 在 nginx 共享内存中存储嵌套表

lua - 当 block 函数返回 nil 时 load() 的行为

c - 线程上的 Lua setfenv 似乎不起作用

algorithm - 行为树实现

android - Corona SDK Google Play 许可证检查

error-handling - 如何正确打印来自 `luaL_dostring()`的错误消息?

go - golang中父类实现方法调用子类方法