Writing a proper Lua 5.1 module with functions - module

I'm working with a program that has standard Lua 5.1 embedded and am trying to write a module I can call functions from but have had no avail.
The current environment is quite picky, and if I make mistakes the scripts will break but will not get any errors, so here we go:
I have something like this (inside moduletests.lua):
local _ = {}
function _.prints()
HUD.AddUpdateBoxText("Hello World!", 200) --Equivalent to print()
end
I would then attempt to require this and call it with:
mts = require 'moduletests' --seems to cause no issues
mts.prints() --breaks the scripts
Normally if the require function is incorrect, the scripts will break. The scripts work fine with the require, so I assume it is correct, but on any attempt to call the print function it will break.
On the other hand, I have another lua module installed and have been able to successfully require it and call a function and it is what I modeled my module after. This is the module's code.
Here is how I used it:
moses = require 'moses' --Works
local bok = moses.isInteger(6)
HUD.AddUpdateBoxText(tostring(bok), 700); --Works, outputs "true"
And this worked fine, perfectly as intended. Can someone tell me what is different or wrong with my module, or have any suggestions on how I could make a better functional version of this?
Thank you to everyone, I sincerely appreciate the help!
Much appreciated!

In Lua modules, you have to return something. The reason your code isn't working is because you are trying to call a method from whatever is returned by the module, but since nothing is being returned, an error can only be expected. Try returning the table:
local ar = {}
function ar.prints()
HUD.AddUpdateBoxText("Hello World!", 200) --Equivalent to print()
end
return ar

Related

unable to use imported modules in event_handler lua

I'm writting a basic event handler in lua which uses some code located in another module
require "caves"
script.on_event({defines.events.on_player_dropped_item}, function(e)
caves.init_layer(game)
player = game.players[e.player_index]
caves.move_down(player)
end
)
but whenever the event is triggered i get following error
attempt to index global 'caves' (a nil value)
why is this and how do i solve it?
You open up the module in question and see what it exports (which global variables are assigned and which locals are returned in the bottom of the file). Or pester the mod author to create interface.
Lua require(filename) only looks up a file filename.lua and runs it, which stands for module initialization. If anything is returned by running the file, it is assigned into lua's (not-so) hidden table (might as well say, a cache of the require function), if nothing is returned but there was no errors, the boolean true is assigned to that table to indicate that filename.lua has already been loaded before. The same true is returned to the variable to the left of equals in the caves = require('caves').
Anything else is left up to author's conscience.
If inside the module file functions are written like this (two variants shown):
init_layer = function(game)
%do smth
end
function move_down(player)
%do smth
end
then after call to require these functions are in your global environment, overwriting your variables with same names.
If they are like this:
local init_layer = function(game)
%do smth
end
local function move_down(player)
%do smth
end
then you won't get them from outside.
Your code expects that the module is written as:
caves = {
init_layer = function(game)
%do smth
end
}
caves.move_down=function(player)
%do smth
end
This is the old way of doing modules, it is currently moved away, but not forbidden. Many massive libraries like torch still use it because you'd end up assigning them to the same named globals anyway.
Кирилл's answer is relevant to newer style:
local caves={
%same as above
}
%same as above
return caves
We here cannot know more about this. The rest is up to you, lua scripts are de-facto open-source anyways.
Addendum: The event_handler is not part of lua language, it is something provided by your host program in which lua is embedded and the corresponding tag is redundant.
You should consult your software documentation on what script.on_event does in this particular case it is likely does not matter, but in general the function that takes another function as argument can dump it to string and then try to load it and run in the different environment without the upvalues and globals that the latter may reference.
require() does not create global table automatically, it returns module value to where you call this function. To access module via global variable, you should assign it manually:
local caves = require "caves"

A program to enable local by default variables

So I've become annoyed at the global by default style in Lua. I'm trying to write a program which will make all programs that run after it incapable of creating global variables. When they try to, that variable will be set to the function environment of that program. I've come up with this, and it seems to work, but for some reason is throwing an error in [edit: 9] in the ComputerCraft rom/programs/edit. When I run a test program,
a = 1
print(a)
it works fine and prevents the global variable from being created while still allowing it to be accessed by that program, but it doesn't work for other programs. I've tried doing _G.a, local a, and other things, but all of them work. Does anyone have any ideas why it might not work on other programs?
local oldload = loadfile
function _G.loadfile(str)
local func = oldload(str)
local env = {}
env._G = env
setmetatable(env, {__index = _G, __newindex =
function(table, var, val)
rawset(env, var, val)
end})
setfenv(func, env)
return func
end
Lua is designed to be an embedded language. This means that the ultimate arbiter is the host language, C.
One Lua script can achieve dominance over another via sandboxing techniques. You didn't fully sandbox your scripts. You changed loadfile, but you didn't change load or dofile.
But that doesn't matter. Why? Because in terms of dominance, C always wins. See, C doesn't call the Lua loadfile function. Well, it can obviously, but it usually doesn't. Instead it calls the Lua API luaL_loadfile.
Lua code can establish a sandbox for other Lua code that it directly loads. But a Lua-based sandbox has no effect on C code, unless that code is deliberately designed to live in a Lua sandbox. And most C libraries aren't.
What this means is that, once you decide to run in a C execution environment that you can't control, your Lua sandbox means nothing. The C code can, and in many cases will, load scripts outside of your control and give them whatever environments that it wants.
And there's nothing you can do about it from Lua. The only way to resolve this is to modify the Lua .dll itself to establish your sandbox.
I found out the solution. It turns out that the shell API, an API used in many ComputerCraft programs, was not in _G, thus was not accessible when I applied the sandbox. Here's my new, functional code:
local oldload = load
function _G.load(str, arg1, arg2, arg3)
local func = oldload(str, arg1, arg2, arg3)
local env = getfenv(func)
if (env == _G) then
env = {}
env._G = env
end
setmetatable(env, {__index = _G, __newindex = function(table, var, val) rawset(env, var, val) end})
setfenv(func, env)
return func
end

Using a function without returning a value visual basic

I'm making a batch development kit in visual basic and i need to be able to call a function that sets textboxes to a saved files text. How do i do this without returning? I tried this, and it lets me run the program, but gives me a warning, not an error. How do i go about doing this? Here is my little function design. P.S. I recently switched back to VB from Java and i'm so used to doing public void. Thanks in advance!
Public Function loadProject()
End Function
You want a Sub, which is the equivalent to the Java void method.
Public Sub LoadProject()
End Sub
It's not a bad Idea to just have a function that returns a value like a success statement just in case you need it. A call to the function doesn't have to accept or use the return value from the function.
You could even build a class with two values - txtpreviousvalue and txtnewvalue
Have your function return that type and fill an instance of the type with the respective values.
One day, if you need it, you'll have it.
P.S. I'm only posting this as an answer because the good answer posted by sstan is not marked as the answer; You should probably do that.

Lua loading variables when calling program ~ Parameter?

I recently got into Computer Craft (Mod for Minecraft) you can code the computers with lua.
I want to go "water status" and it will load "water" and then set a variable to "status" or any other word/string in its place so I can use it for anything. I guess you would call that a parameter?
os.run( environment, prgmpath, arguments )
I don't understand what environment is. prgmpath is water and the argument would be status?
I'm just unsure how to grab those arguments from that.
So yeah, I'm quite confused =/
Tried to explain it best I could, thanks,
Jazza
After searching around, I think I found my answer.
lua water arg1
Goes in the command line
derp = arg[2]
Goes in the file?
EDIT: After lurking around some more, I found out that:
derp = ...
print(derp)
In the file and:
file hi
It printed hi, so I guess that works, but I can't seem to add any more D=
os.run is an extension to the os library written specifically for that mod. according to the documentation on the wiki:
environment is the metatable to set up the state for the script you're running at prgmpath
arguments is whatever you want to pass to the code you're calling located in the script at prgmpath
so basically, if you had some code set up to do something specific in path/to/file.lua, but it depended on some outside state, you'd set up that state in your calling file, and pass the environment (or a subset of it) to the code in file.lua by passing it as a table to the first param in os.run().
arguments is supposed to be a table of arguments you wanted to pass to the function you'd be calling in file.lua. so if in file.lua you had...
function doSomething(arg1, arg2, arg3)
...
end
you'd pass arg1, arg2, and arg3 to doSomething by creating a table and passing it like this...
local args = {"arg1Val", {}, 1234}
os.run({}, '/path/to/file.lua', args)
os.run would then set up an empty environment for function doSomething() in file.lua, and pass the 3 values in args to the function.
make sense?

Calling PythonFunction's from a VB application hosting Iron Python

I'm a C++ programming who was tapped to write a small application in Visual Basic. The application hosts an IronPython runtime and I am attempting to define some function in python and then call them from VB. I have written a simple test function in python
def test():
print "Test was Called"
Then I use the iron python to create a ScriptSource from the python file. I am able to look up the "test" variable through object operations but I can not figure out how to call the object that. For example (in VB):
pyScope = engine.CreateScope()
pySource = engine.CreateSourceFromFile("C:\Some\File\path\test.py")
pySource.Execute(pyScope)
' Now I expect the function test() to be defined in pyScope
Dim tmp as Object
pyScope.TryGetVariable("test", tmp)
At this point in my code tmp is defined as an object of type PythonFunction. I can't figure out how to call this function.
tmp()
Is not valid VB syntax. I've gotten this far, now how do I perform this seemingly simple task?
Edit: By calling
pyEngine.Operations.Invoke(tmp)
I am able to call the function and I see the expected output at stdout. I am still under the impression that there is some function-pointer-like type that I can cast objects of type PythonFunction to which will let me invoke temp directly without calling to the Python engine.
Not sure this will work, but try casting it to an Action type:
DirectCast(tmp, Action)()
Based on the comment, try this:
engine.ObjectOperations.Invoke(tmp, Nothing)
VB in .NET 4 should have the same dynamic support as C#. According to http://msdn.microsoft.com/en-us/library/ee461504.aspx#Y5108 (near the bottom), you should be able to do:
Dim tmp As Object = scope.GetVariable("test")
... which is what you're already doing, so make sure you're targeting .NET 4.
If that doesn't work you should be able to cast it with the generic version of GetVariable:
Dim tmp As Action = scope.GetVariable(Of Action)("test")
Finally, you already discovered Invoke on ObjectOperations.
(You may need to tweak the syntax, since I don't know VB.)