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

Is there a way to have one program edit values in another program?

Started by Canaries, 09 April 2016 - 01:27 PM
Canaries #1
Posted 09 April 2016 - 03:27 PM
G'day all, I'm a long time lurker, first time poster. For the most part, every question I've had has been answered already, but I've been unable to find any info on this:

Is it possible, to say, have two programs: test1 and test2,

test2 would look something like this:

a = 1
if a == 2 then
print("Yay, a success!!!")

and test1 would somehow be able to change the value of a inside of test2, and then run test2?

I've tried shell.run("edit") and whilst that could be useful, it requires user input, and I'd like to avoid that, as I want this for a basic banking system.

For anyone interested, my system will not be a backed system, so people will exchange virtual credits, not physical items, which seems to be how most banking systems work.

Any input is much appreciated, and thanks for the great community
Bomb Bloke #2
Posted 09 April 2016 - 05:27 PM
When ComputerCraft "runs a script", what it's really doing is reading the content of a text file (with your source code in it), compiling that into a function, and then executing that function. This process is done in Lua code, and you can replicate it yourself: see the source of shell.run() and os.run(). It helps to be familiar with environment tables - you'll notice os.run() is setting a different environment to the usual _G, which the shell takes advantage of to provide a unique table for each instance of the shell script that's actually running on the computer (as there can be more than one - you can have shell run shell, for example, or use multishell to put multiple shells into different tabs).

So the idea is that you can load your script file, use the string API to edit it to taste, and then compile the result with loadstring().

Of course, it sounds like it'd be easier to have the script you want to "edit" instead read values from a config file, or something…
Canaries #3
Posted 10 April 2016 - 01:45 AM
Thanks for the reply! It's a lot to digest, and I feel like I've got a lot of reading to do, but at least now I'm on the right path, so thanks for the input
Canaries #4
Posted 11 April 2016 - 11:20 AM
When ComputerCraft "runs a script", what it's really doing is reading the content of a text file (with your source code in it), compiling that into a function, and then executing that function. This process is done in Lua code, and you can replicate it yourself: see the source of shell.run() and os.run(). It helps to be familiar with environment tables - you'll notice os.run() is setting a different environment to the usual _G, which the shell takes advantage of to provide a unique table for each instance of the shell script that's actually running on the computer (as there can be more than one - you can have shell run shell, for example, or use multishell to put multiple shells into different tabs).

So the idea is that you can load your script file, use the string API to edit it to taste, and then compile the result with loadstring().

Of course, it sounds like it'd be easier to have the script you want to "edit" instead read values from a config file, or something…

I'm at a bit of a loss here: I'm not entirely sure how to use the string API? Whilst being able to change variables in a string does seem like what I want to do, I'm not sure if/how to use it inside of another program.

And as for configs, any searches result in nothing but the CC mod config, and changing values in it like WiFi range.

If I could have a simple slice of example code, that'd be greatly appreciated? I just don't quite understand how loadstring() is meant to be used.

Again, thanks for any and all help
Bomb Bloke #5
Posted 11 April 2016 - 02:13 PM
The string API offers a wide variety of functions for dealing with text, and can help you search through and alter it in many ways. The idea is that you load the target script file as text, find the values you want to change, and then you change them; you can then pass the resulting string to loadstring() in order to have it compiled into an actual Lua function you may then call.

This is not considered "simple". The amount of "edge cases" you'd need to account for - odd-ball scenarios, such as the script you're wanting to change having eg a capital letter in an unexpected and/or inconvenient location - are potentially innumerable, depending on the number and type of the changes you wish to make. If you have full control over the content of both scripts then you can ensure these problems never happen and so cut down on "edge case"-handling code, but if you had such control I imagine you wouldn't be trying all this in the first place? You'd just have script one write script two in its entirety and call it a day, yeah?

The idea is that a config file directly addresses the above problem by introducing a third file which both of the other scripts agree will be formatted in a certain simple manner. You don't need to include a ton of complex parsing code (just a bit of simple parsing code) and you don't need to include all the source for one script in the other. Ever played with INI files? They're an example: you can make the scripts which make use of the configs as complex as you like and this won't make it any more difficult to alter the configs themselves.

A very basic example:

test1
local configFile = fs.open("config.ini", "w")  --# You don't *have* to use "ini" in your file name, this is just an example.
configFile.writeLine("2")
configFile.close()

shell.run("test2")

test2
local configFile = fs.open("config.ini", "r")  --# http://www.computercraft.info/wiki/Fs.open
local a = tonumber(configFile.readLine())  --# Even if you wrote a number-type variable to the file, everything gets read back as a string; tonumber() can convert
configFile.close()

if a == 2 then
  print("Yay, a success!!!")
end

If you want to pass on multiple values, textutils.serialise() offers a great shortcut: you can stick a bunch of values into a table, serialise it into a single string which you can write in one go, and then later read and unserialise that string:

test1
local myTable = {["a"] = 2, ["b"] = 3, ["someOtherKey"] = "strings, why not?"}

local configFile = fs.open("config.ini", "w")
configFile.writeLine(textutils.serialise(myTable))
configFile.close()

shell.run("test2")

test2
local configFile = fs.open("config.ini", "r")
local myTable = textutils.unserialise(configFile.readAll())
configFile.close()

if myTable.a == 2 and myTable.b = 3 and myTable.someOtherKey == "strings, why not?" then  --# Note that the types of all the values *in* the table are preserved this way.
  print("Yay, a success!!!")
end