This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
cmdpwnd's profile picture

Setting file environment?

Started by cmdpwnd, 07 April 2016 - 01:25 AM
cmdpwnd #1
Posted 07 April 2016 - 03:25 AM
So I was looking a bit into setfenv and getfenv (still unsure whether or not it is applicable to my scenario) because I'm trying to set a global table for the file/function that I'm going to use, which setfenv apparently can do. However I'm making not an explicit compiler but a file that will copy the contents of other files and set their environment after some variable manipulation has occured. So far I've been able to read the file into a variable which is appended to a table (libMem in example) localized in the file of the script currently running and then from that table copy the keys and values to essentially make them as if they are accessible in the same scope as that table (here same scope a libMem). In effect it does this.


local libMem = {}
local globalize = 0
local function init()
    --read all files in a directory. lets pretend returns only one file: /lib/file.lua
    local libDir = fs.list('/lib')
    for _,file in ipairs(libDir) do
        libMem[string.gsub(tostring(file),'.lua','') = readFile(tostring('/lib'..file)) --chop off .lua extension and append to libMem
    end
    globalize = 1
end

local function readFile(path)
    local f = fs.open(path,'r')
    local contents = f.readAll()
    f.close()
    return contents
end

init()
if globalize == 1 then
    for k,v in ipairs(libMem) do k = v end
end


My problem however, is getting my variable manipulations (k=v) to be accessible to the other files which I will run with the exact same varName, I don't want to use os.loadAPI to load my script to the 'real' _G first and then access them as it may overwrite other _G indices and I'm not a fan. Overriding os.loadAPI may be an option if I use setfenv for the file(s) being appended but I'm not sure how this would work and need a bit of understanding of setfenv and getfenv to go along with it if possible.

Thanks :)/>/>/>/>/>
Bomb Bloke #2
Posted 07 April 2016 - 04:06 AM
Long story short, each function has a table that it dumps its globals into, termed its "environment table". getfenv() gets you a pointer to that function's table, whereas setfenv() sets it to a different pointer. Simple as that, though there are some optional parameters you can use to try and target a function's parent's table and so on.

This loop here:

for k,v in ipairs(libMem) do k = v end

… could perhaps use some further explanation? I'm guessing you meant to do:

for k,v in ipairs(libMem) do libMem[k] = v end

… but I can't see any practical purpose to that, either. Each instance of v contains a string representing code that could presumably be loadstring()'d into a function, but even if you were doing that at some point I'm not entirely clear on what you want accessible from where.

It sorta sounds like you want to simply have all the functions you load share the one environment table? If so, you'd simply do something like this:

local sharedEnv = {}

local function readFile(path)
    local newFunc = loadfile(path)
    setfenv(newFunc, sharedEnv)
    return newFunc
end

Or, using the Lua 5.2 version of loadfile() (which can set the environment directly, and indeed must, as setfenv() doesn't exist in that build):

local sharedEnv = {}

local function readFile(path)
    return loadfile(path, "t", sharedEnv)
end

Note that as of ComputerCraft 1.74 you've got access to most of 5.2's functionality, in addition to 5.1's, when it comes to setting environments - so either of the above should work. There are noises that ComputerCraft will one day shift away from 5.1, but it beats me when that'll ever happen. Frankly I feel the current setup (with the compatibility layer in place) gives us the best of both worlds, but then, I don't often have cause to play with environment tables.
cmdpwnd #3
Posted 07 April 2016 - 04:15 AM

local sharedEnv = {}

local function readFile(path)
    local newFunc = loadfile(path)
    setfenv(newFunc, sharedEnv)
    return newFunc
end

I believe this is what I was looking for! Thank you BB, you always seem to have the best advice/solutions! :)/> <3