I'm using Corona SDK for the first time and read up on how to call functions from other files, but I seem to be having issues. Here are the two scripts so far:
timer.lua
local M = {}
function M.Timer(n, count) --(period, how many times repeated)
if count > 0 then
local iter= os.time()+n
while iter ~= os.time() do
end
M.onTime(count)
count = count - 1
M.Timer(n,count)
end
end
function M.onTime(count)
display.newtext(count,250,50,native.systemFont,16)
end
return M
main.lua
local timeTool = require("timer")
timeTool.Timer(1,5)
They are located in the same directory. When I run main.lua on the simulator, I get the error attempt to call field 'Timer' (a nil value). This leads me to believe that the main file failed in acquiring the contents of the timer script, but from what I've seen, I am using the correct syntax. Is there something I missed, or am I using the wrong method for calling functions from other scripts?
Related
I'm quite new to Lua and Embedded programming. I'm working on a project:
IoT node that can be abstractedinto two parts: sensor and a board that runs Open WRT with Lua 5.1. I'm writing script that would be called from crontab every minute.
In my script I'm accessing data from the sensor via package written with C. The result of reading data from sensor is 'hexadecimal numbers returned in string:
4169999a4180cccd41c9851f424847ae4508e0003ddb22d141700000418e666641c87ae14248147b450800003dc8b439
Then convert it (string) to values I need and POST it to API.
Problem:
Sometimes API is not reachable due to poor network connection.
So I need to implement system where I would read a data from a sensor and then if API is not responding, I would save it to a FIFO queue (buffer). And then next time when script is called to read it would be sending 'old' records first and the newest one and the end.
local queue_filespec = [[/path/to/your/queue/file]]
-- Initially your "queue file" (regular file!) must contain single line:
-- return {}
local function operation_with_queue(func)
local queue = dofile(queue_filespec)
local result = func(queue)
for k, v in ipairs(queue) do
queue[k] = ("%q,\n"):format(v)
end
table.insert(queue, "}\n")
queue[0] = "return {\n"
queue = table.concat(queue, "", 0)
local f = assert(io.open(queue_filespec, "w"))
f:write(queue)
f:close()
return result
end
function add_to_queue(some_data)
operation_with_queue(
function(queue)
table.insert(queue, some_data)
end
)
end
function extract_from_queue()
-- returns nil if queue is empty
return operation_with_queue(
function(queue)
return table.remove(queue, 1)
end
)
end
Usage example:
add_to_queue(42)
add_to_queue("Hello")
print(extract_from_queue()) --> 42
print(extract_from_queue()) --> Hello
print(extract_from_queue()) --> nil
-- This is some of the code made by Roblox themselves:
-- Setup table that we will return to scripts that require the ModuleScript.
local PlayerStatManager = {}
-- Table to hold all of the player information for the current session.
local sessionData = {}
-- Function the other scripts in our game can call to change a player's stats. This
-- function is stored in the returned table so external scripts can use it.
function PlayerStatManager:ChangeStat(player, statName, changeValue)
sessionData[player][statName] = sessionData[player][statName] + changeValue
end
-- Function to add player to the sessionData table.
local function setupPlayerData(player)
sessionData[player] = {Money = 0, Experience = 0}
end
-- Bind setupPlayerData to PlayerAdded to call it when player joins.
game.Players.PlayerAdded:connect(setupPlayerData)
-- Return the PlayerStatManager table to external scripts can access it.
return PlayerStatManager
--------------------------------------------------------------------------------
-- Require ModuleScript so we can change player stats
local PlayerStatManager = require(game.ServerStorage.PlayerStatManager)
-- After player joins we'll periodically give the player money and experience
game.Players.PlayerAdded:connect(function(player)
while wait(2) do
PlayerStatManager:ChangeStat(player, 'Money', 5)
PlayerStatManager:ChangeStat(player, 'Experience', 1)
end
end)
When I run these two script, it run perfectly, adding the print(sessionData[player][statName]) inside the ChangeStat function, but when I removed the game.Players.PlayerAdded:connect(setupPlayerData) part in the module script, it stopped working. I though module script does not execute code without it being called, and if that was the case, shouldn't the game.Players.PlayerAdded:connect(setupPlayerData) part be delay and not function since player's already added, therefore it not firing?
require executes the required code.
If that was not the case you would not be able to get a table through require, as your return PlayerStatManager statement would not be executed.
As a consequence removing
-- Bind setupPlayerData to PlayerAdded to call it when player joins.
game.Players.PlayerAdded:connect(setupPlayerData)
will cause an added player not to be initialized properly. This basically says: when a new player is added, call setupPlayerData.
Where setupPlayerData says: give a new set of stats to player
As you removed that line no player has stats. If you don't have stats you can't increase their values...
So obviously you did not understand what the code did befor you changed it. Therefor you cannot understand why your changes cause problems.
If you change a system you don't understand you can be lucky, but in most cases you will utterly fail.
It is my understanding that in Lua 5.2 that environments are stored in upvalues named _ENV. This has made it really confusing for me to modify the environment of a chunk before running it, but after loading it.
I would like to load a file with some functions and use the chunk to inject those functions into various environments. Example:
chunk = loadfile( "file" )
-- Inject chunk's definitions
chunk._ENV = someTable -- imaginary syntax
chunk( )
chunk._ENV = someOtherTable
chunk( )
Is this possible from within Lua? The only examples I can find of modifying this upvalue are with the C api (another example from C api), but I am trying to do this from within Lua. Is this possible?
Edit: I'm unsure of accepting answers using the debug library. The docs state that the functions may be slow. I'm doing this for efficiency so that entire chunks don't have to be parsed from strings (or a file, even worse) just to inject variable definitions into various environments.
Edit: Looks like this is impossible: Recreating setfenv() in Lua 5.2
Edit: I suppose the best way for me to do this is to bind a C function that can modify the environment. Though this is a much more annoying way of going about it.
Edit: I believe a more natural way to do this would be to load all chunks into separate environments. These can be "inherited" by any other environment by setting a metatable that refers to a global copy of a chunk. This does not require any upvalue modification post-load, but still allows for multiple environments with those function definitions.
The simplest way to allow a chunk to be run in different environments is to make this explicit and have it receive an environment. Adding this line at the top of the chunk achieves this:
_ENV=...
Now you can call chunk(env1) and later chunk(env2) at your pleasure.
There, no debug magic with upvalues.
Although it will be clear if your chunk contains that line, you can add it at load time, by writing a suitable reader function that first sends that line and then the contents of the file.
I do not understand why you want to avoid using the debug library, while you are happy to use a C function (neither is possible in a sandbox.)
It can be done using debug.upvaluejoin:
function newEnvForChunk(chunk, index)
local newEnv = {}
local function source() return newEnv end
debug.upvaluejoin(chunk, 1, source, 1)
if index then setmetatable(newEnv, {__index=index}) end
return newEnv
end
Now load any chunk like this:
local myChunk = load "print(x)"
It will initially inherit the enclosing _ENV. Now give it a new one:
local newEnv = newEnvForChunk(myChunk, _ENV)
and insert a value for 'x':
newEnv.x = 99
Now when you run the chunk, it should see the value for x:
myChunk()
=> 99
If you don't want to modify your chunk (per LHF's great answer) here are two alternatives:
Set up a blank environment, then dynamically change its environment to yours
function compile(code)
local meta = {}
local env = setmetatable({},meta)
return {meta=meta, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
block.meta.__index=scope
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
Set up a blank environment, and re-set its values with your own each time
function compile(code)
local env = {}
return {env=env, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
for k,_ in pairs(block.env) do block.env[k]=nil end
for k,v in pairs(scope) do block.env[k]=v end
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
Note that if micro-optimizations matter, the first option is about 2✕ as slow as the _ENV=... answer, while the second options is about 8–9✕ as slow.
I was trying to log in using LibCurl. Actually I am using LuaCurl the binding of libCurl in Lua. I am referring to this web page: http://www.hackthissite.org/articles/read/1078
I tried this:
> require("libcurl")
> c=curl.new()
> c:setopt(curl.OPT_USERAGENT,"Mozilla/4.0")
> c:setopt(curl.OPT_AUTOREFERER,true)
> c:setopt(curl.OPT_FOLLOWLOCATION,true)
> c:setopt(curl.OPT_COOKIEFILE,"")
> c:setopt(curl.OPT_URL,"https://www.chase.com")
> res=c:perform()
But after this last operation the program is stuck as if waiting for something. What am I doing wrong here?
Thanks
I tried your program and it seems to work fine. What I get is the content of the given site pumped to stdout. It seems you are just having networking issues...
If you want to capture the whole output as a string and process it later, you have to provide a callback using OPT_WRITEFUNCTION which will be called with more data which you can save. Here is a simplified version of how I implemented the GET method in my simple web-mining toolbox WDM.
local c = curl.new()
...
function get(url)
c:setopt(curl.OPT_URL,url)
local t = {} -- output will be stored here
c:setopt(curl.OPT_WRITEFUNCTION, function (a, b)
local s
-- luacurl and Lua-cURL friendly way
if type(a) == "string" then s = a else s = b end
table.insert(t, s) -- store this piece of data
return #s
end)
assert(c:perform())
return table.concat(t) -- return the whole content
end
I have some lua code in a file. I want to create multiple closure instances of this code, each with a different _ENV upvalue. I can use luaL_loadfile to load the file and set the first upvalue, N times with different tables, to create N instances. But wouldn't this load and compile the file N times?
The lua equivalent of what i want to do is the following, except without the loadfile
func_list = {}
for i = 1, 10 do
local new_env = {hello=i, print=print}
func_list[i] = loadfile("Code.lua", "t", new_env)
end
for i = 1, 10 do
func_list[i]()
end
------ Code.lua ------
print(hello*hello)
is there a better way to do this?
Whenever you load a string/file in Lua, what you get in return is a function to call to actually run the file. What load does for you is just some additional processing to set the _ENV.
However, nothing prevents you from setting _ENV yourself. You could do it with something like this:
-- Code.lua --
_ENV = ...
print(hello * hello)
Then, you could load/compile the file just once, and use multiple instances as such:
local code = loadfile("Code.lua")
env_list = {}
for i = 1, 10 do
local new_env = {hello=i, print=print}
code(new_env)
env_list[i] = new_env
end
If you do not want the user to write _ENV = ... in every file, you could instead load the file into a string, prepend the line yourself and use load to compile the source. But this would not work on compiled files.
Use the IO libraries to load the file into a string, and then call loadstring on it.
Alternatively, just get one chunk and then change it's env prior to executing it