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

load() file in current environment?

Started by Dave-ee Jones, 24 October 2017 - 08:52 PM
Dave-ee Jones #1
Posted 24 October 2017 - 10:52 PM
Hoi!

This question might seem really complicated in the title, but it's not. Sorry, I couldn't find the words to describe this problem.

So, I've got a file that needs to be run in another file's context. E.g.

File 1 declares this variable and loads File 2's contents and runs them.

local LOCALS = {}
local file2 = loadfile(file2)
file2()

File 2 references an index in that variable:

if LOCALS["test"] == "this is a test" then
  print("True!")
else
  print("False!")
end

But it just comes up with this error:
string:1: attempt to index ? (a nil value)

Obviously because it doesn't recognise the 'LOCALS' table. I don't want to make everything global, so how can I run File2 as if it was part of File1's code (without putting that code in File1) so that it does recognise File1's variables?

I would assume this has been answered before because it seems like a pretty obvious question in some cases..Unless not?

Anyway, any help would be appreciated! :)/>
SquidDev #2
Posted 24 October 2017 - 11:58 PM
The whole point of locals is, well, that they're local - you can't access them elsewhere. If you don't want to use globals, but still want access to particular variables then you could pass them as arguments:


--# File1
local LOCALS = {}
local file2 = loadfile(file2)
file2(LOCALS) --# pass in LOCALS as an argument

--# File 2
local LOCALS = ... --# Read in LOCALS as an argument
print(LOCALS["test"])

If you try to overwrite LOCALS, it won't propagate to other files (as it's just a local variable in each) but you can still modify the fields to your heart's desire.
Dave-ee Jones #3
Posted 25 October 2017 - 01:52 AM
The whole point of locals is, well, that they're local - you can't access them elsewhere. If you don't want to use globals, but still want access to particular variables then you could pass them as arguments:


--# File1
local LOCALS = {}
local file2 = loadfile(file2)
file2(LOCALS) --# pass in LOCALS as an argument

--# File 2
local LOCALS = ... --# Read in LOCALS as an argument
print(LOCALS["test"])

If you try to overwrite LOCALS, it won't propagate to other files (as it's just a local variable in each) but you can still modify the fields to your heart's desire.

Ah, cheers, that should work perfectly fine!
However, what if I was to call a function in File1 from File2?
Edited on 24 October 2017 - 11:53 PM
Kepler #4
Posted 25 October 2017 - 03:47 AM
When loadfile is called, the function that is loaded can have access to the same _ENV as the calling function.

_ENV is not the same as _G (global)

Since craft OS does not sandbox well, you can create your own sandbox in your program:

local function main()
  _ENV.LOCALS = { }

  function _ENV.accessibleFromFile2()
	print('called')
  end

  local file2 = loadfile('file2', _ENV)
  file2()
end

-- make a copy of the current env
local env = setmetatable({ }, { __index = _G })
for k,v in pairs(_ENV) do
  env[k] = v
end

-- set the env for main
setfenv(main, env)
main()

In file2, you can just call accessibleFromFile2() – (without using the _ENV prefix).

Here's some sample contents:
for k,v in pairs(_ENV) do
  print(k)
end
accessibleFromFile2()

Although, I really think you should look at using require as what you are attempting to do is not good practice.
Edited on 25 October 2017 - 01:50 AM
Lupus590 #5
Posted 25 October 2017 - 01:02 PM
However, what if I was to call a function in File1 from File2?

Functions can be passed like variables (in fact, Lua makes few distinctions between variables and functions, a function is a variable with executable code in it).
Edited on 25 October 2017 - 11:03 AM
Bomb Bloke #6
Posted 26 October 2017 - 05:35 AM
Sorta. The idea is that when you define a function, you're also creating a pointer value, and that's what gets passed back by the constructor. Since you can assign that pointer to a variable (same as any other value), it can be passed around in exactly the same manner as a string or a number or whatever. Remember, variables are for assigning things to: those "things" aren't variables themselves.

It's also worth noting that creating duplicate copies of a function pointer doesn't create multiple copies of the function itself, and that coroutines and (more importantly) tables work in the exact same manner:

local myTablePointer = {}   -- Pointer to a new table gets assigned to the "myTable" variable

local myCopyOfTheTablePointer = myTablePointer   -- Pointer value gets copied to "myCopyOfTheTablePointer"

myCopyOfTheTablePointer[1] = "Hello World"

print(myTablePointer[1])   -- "myTablePointer" and "myCopyOfTheTablePointer" variables contain pointer values leading to the same table, so this prints "Hello World"

Usually when discussing table/function/coroutine pointers they're just referred to as tables/functions/coroutines instead, but this is purely for the sake of convenience.
Dave-ee Jones #7
Posted 27 October 2017 - 12:50 AM
Thanks for all the replies and suggestions! I went with this:


local _func = load("/test")
_func(LOCALS,COLOURS)

And had "/test" load the arguments. It kinda reminds me of PHP, passing strings and things to a PHP file to deal with :P/>

However, I am using another file which doesn't support 'os.loadAPI' and therefore I have to use the 'dofile' function to call it as an API so I can reference it's functions. Not ideal, probably uses up 12kB of RAM..

It's also worth noting that creating duplicate copies of a function pointer doesn't create multiple copies of the function itself, and that coroutines and (more importantly) tables work in the exact same manner:

local myTablePointer = {}   -- Pointer to a new table gets assigned to the "myTable" variable

local myCopyOfTheTablePointer = myTablePointer   -- Pointer value gets copied to "myCopyOfTheTablePointer"

myCopyOfTheTablePointer[1] = "Hello World"

print(myTablePointer[1])   -- "myTablePointer" and "myCopyOfTheTablePointer" variables contain pointer values leading to the same table, so this prints "Hello World"

Usually when discussing table/function/coroutine pointers they're just referred to as tables/functions/coroutines instead, but this is purely for the sake of convenience.

I'd never thought of it that way, but now that I think about it it makes more sense. Referring to a single table with a line like this:

print(TABLE1[TABLE2[x]])
Makes it really confusing sometimes :P/>
Edited on 26 October 2017 - 10:52 PM