lua function attempt to call global (a nil value) - function

I am new to lua and i am trying to make a function that receives documents and outputs a table but I am getting the above error. Why??
io.write("How many documents are we evaluating? \nInput: ")
local total_documents=io.read("*n")
io.read()
local docTable = {}
inputDocument()
function inputDocument()
local input
local file
local inputFile = {filename = nil, contents = nil, wordcount = nil}
repeat
io.write("Please enter document (filename.extension): ")
input = io.read()
file =io.open(input)
if file == nil then
print("File does not exist try again")
end
until(file ~=nil)
inputFile.filename = input
return inputFile
end

You need to define inputDocument before using it:
function inputDocument()
...
end
io.write("How many documents are we evaluating? \nInput: ")
...
inputDocument()

Related

How to connect to a web service that returns JSON in LUA

I am beginner in LUA. I have written the following code in lua file that mysql proxy run with it.
function read_query(packet)
if string.byte(packet) == proxy.COM_QUERY then
local command = string.lower(packet)
if string.find(command, "select") ~= nil and string.find(string.lower(packet), "from") ~= nil then
local socket = require('socket')
local conn, err = socket.connect('localhost', 5050)
print(conn, err)
proxy.response.type = proxy.MYSQLD_PACKET_OK
//proxy.response.resultset get json from web service (url)
proxy.response.resultset = {
fields = {
{ type = proxy.MYSQL_TYPE_INT, name = "id", },
},
rows = {
{ 9001 }
}
}
return proxy.PROXY_SEND_RESULT
end
end
end
I want to connect to the web service on Port 5050 that return the JSON file and save the json that it returns in the proxy.response.resultset.
Another question, How can i add socket module. I paste files like following image
socket module files
but give an error : can not find /socket/core.lua.
local socket = require('socket')
You are using luasocket and it comes as a mix of lua (socket.lua) and binary (socket/core.so) files. You need to set (if it's not set already) to point to .so files; something like this may work: package.cpath=package.cpath..';./?.so'

Redis Lua Differetiating empty array and object

I encountered this bug in cjson lua when I was using a script in redis 3.2 to set a particular value in a json object.
Currently, the lua in redis does not differentiate between an empty json array or an empty json object. Which causes serious problems when serialising json objects that have arrays within them.
eval "local json_str = '{\"items\":[],\"properties\":{}}' return cjson.encode(cjson.decode(json_str))" 0
Result:
"{\"items\":{},\"properties\":{}}"
I found this solution https://github.com/mpx/lua-cjson/issues/11 but I wasn't able to implement in a redis script.
This is an unsuccessful attempt :
eval
"function cjson.mark_as_array(t)
local mt = getmetatable(t) or {}
mt.__is_cjson_array = true
return setmetatable(t, mt)
end
function cjson.is_marked_as_array(t)
local mt = getmetatable(t)
return mt and mt.__is_cjson_array end
local json_str = '{\"items\":[],\"properties\":{}}'
return cjson.encode(cjson.decode(json_str))"
0
Any help or pointer appreciated.
There are two plans.
Modify the lua-cjson source code and compile redis, click here for details.
Fix by code:
local now = redis.call("time")
-- local timestamp = tonumber(now[1]) * 1000 + math.floor(now[2]/1000)
math.randomseed(now[2])
local emptyFlag = "empty_" .. now[1] .. "_" .. now[2] .. "_" .. math.random(10000)
local emptyArrays = {}
local function emptyArray()
if cjson.as_array then
-- cjson fixed: https://github.com/xiyuan-fengyu/redis-lua-cjson-empty-table-fix
local arr = {}
setmetatable(arr, cjson.as_array)
return arr
else
-- plan 2
local arr = {}
table.insert(emptyArrays, arr)
return arr
end
end
local function toJsonStr(obj)
if #emptyArrays > 0 then
-- plan 2
for i, item in ipairs(emptyArrays) do
if #item == 0 then
-- empty array, insert a special mark
table.insert(item, 1, emptyFlag)
end
end
local jsonStr = cjson.encode(obj)
-- replace empty array
jsonStr = (string.gsub(jsonStr, '%["' .. emptyFlag .. '"]', "[]"))
for i, item in ipairs(emptyArrays) do
if item[1] == emptyFlag then
table.remove(item, 1)
end
end
return jsonStr
else
return cjson.encode(obj)
end
end
-- example
local arr = emptyArray()
local str = toJsonStr(arr)
print(str) -- "[]"

How to send multiple data (conn:send()) with the new SDK (NodeMCU)

I've been reading the NodeMCU documentation and several closed issues about the change of SDK that previouly allowed to send multiple data streams (acting like a queued net.socket:send).
It seems a huge debate grew here (#730) and there (#993) or even here (#999). However, I did not find any convincing example of a webserver code that would allow me to read multiple html files (e.g. head.html and body.html) to display a page. Here's the example from TerryE that I tried to adapt, but with no success:
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on ("receive", function(sck, req)
local response = {}
local f = file.open("head.html","r")
if f ~= nil then
response[#response+1] = file.read()
file.close()
end
local f = file.open("body.html","r")
if f ~= nil then
response[#response+1] = file.read()
file.close()
end
local function sender (sck)
if #response>0 then sck:send(table.remove(response,1))
else sck:close()
end
end
sck:on("sent", sender)
sender(sck)
end )
end )
When connecting to the ESP8266, nothing loads and I get no error from the lua terminal.
For your information, head.html contains:
<html>
<head>
</head>
And body.html contains:
<body>
<h1>Hello World</h1>
</body>
</html>
I am very new to NodeMCU, please be tolerant.
Here is my solution without using tables, saving some memory:
function Sendfile(sck, filename, sentCallback)
if not file.open(filename, "r") then
sck:close()
return
end
local function sendChunk()
local line = file.read(512)
if line then
sck:send(line, sendChunk)
else
file.close()
collectgarbage()
if sentCallback then
sentCallback()
else
sck:close()
end
end
end
sendChunk()
end
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on("receive", function(sck, req)
sck:send("HTTP/1.1 200 OK\r\n" ..
"Server: NodeMCU on ESP8266\r\n" ..
"Content-Type: text/html; charset=UTF-8\r\n\r\n",
function()
Sendfile(sck, "head.html", function() Sendfile(sck, "body.html") end)
end)
end)
end)
And this is for serving single files:
function Sendfile(client, filename)
if file.open(filename, "r") then
local function sendChunk()
local line = file.read(512)
if line then
client:send(line, sendChunk)
else
file.close()
client:close()
collectgarbage()
end
end
client:send("HTTP/1.1 200 OK\r\n" ..
"Server: NodeMCU on ESP8266\r\n" ..
"Content-Type: text/html; charset=UTF-8\r\n\r\n", sendChunk)
else
client:send("HTTP/1.0 404 Not Found\r\n\r\nPage not found")
client:close()
end
end
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on ("receive", function(client, request)
local path = string.match(request, "GET /(.+) HTTP")
if path == "" then path = "index.htm" end
Sendfile(client, path)
end)
end)
Thank you for the reply. I actually added the header you mentioned, I didn't know that was necessary and I also removed the sck argument in the sender function. My first code was actually working, I don't know what was wrong last time.
Anyway, it helped me understanding what was happening: the following code seems to concatenate the response array, since the event sent of the socket calls back the sender function (sck:on("sent", sender))
sck:send(table.remove(response,1))
In fact, table.remove(array, 1) returns the first item of the array, and removes this item of the array. Calling this line multiple times has the effect to read through it, item by item.
For the sake of simplicity, here is the code of a simple webserver able to serve multiple files:
header = "HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on ("receive", function(sck, req)
local response = {header}
tgtfile = string.sub(req,string.find(req,"GET /") +5,string.find(req,"HTTP/") -2 )
if tgtfile == "" then tgtfile = "index.htm" end
local f = file.open(tgtfile,"r")
if f ~= nil then
response[#response+1] = file.read()
file.close()
else
response[#response+1] = "<html>"
response[#response+1] = tgtfile.." not Found - 404 error."
response[#response+1] = "<a href='index.htm'>Home</a>"
end
collectgarbage()
f = nil
tgtfile = nil
local function sender ()
if #response>0 then sck:send(table.remove(response,1))
else sck:close()
end
end
sck:on("sent", sender)
sender()
end)
end)
This example was taken from this instructables and fixed to work with the new SDK (which do not allow multiple :send anymore). Please let me know if this code has some issues.
I don't know what is the size limit of the files though. Nevertheless, I manage to append more than 2Ko to the response variable and send it at once without any issue.

mysql-proxy result field manipulation

I have a MYSQL server and MYSQL-PROXY and I am trying to manipualte the results I send to the client as a response to a SELECT query. I have writen this code in lua:
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
function read_query_result(inj)
local fn = 1
local fields = inj.resultset.fields
while fields[fn] do
fn = fn + 1
end
fn = fn - 1
print("FIELD NUMBER: " .. fn)
for row in inj.resultset.rows do
print ("--------------")
for i = 1, fn do
if (string.starts(fields[i].name,"TEST")) then
row[i]="TESTED"
end
print ("DATA: " .. fields[i].name .. " -> " .. row[i])
end
end
return proxy.PROXY_SEND_RESULT
end
I can correctly read the field names and values. I can detect the condition where I want the result modified, but I can not get the data sent to the client.
I see two problems:
I am setting the value in the local row variable, but I have not found the way to set the real resultset (inj.Resultset.row[i] or something similar).
There is something wrong with return proxy.PROXY_SEND_RESULT, because I am seeing that whenever I comment that sentence I see the results, and If I uncomment it I get an error.
I have not found example code as a reference.
Ok. Solved.
Data has to be inserted in a table
PROXY_SEND_RESULT requires proxy.response.type to be set.
This is the correct module:
function read_query_result(inj)
local fn = 1
local fields = inj.resultset.fields
proxy.response.resultset = {fields = {}, rows = {}}
while fields[fn] do
table.insert(proxy.response.resultset.fields, {type = proxy.MYSQL_TYPE_STRING, name = fields[fn].name})
fn = fn + 1
end
fn = fn - 1
for row in inj.resultset.rows do
for i = 1, fn do
if (string.starts(fields[i].name,"TEST")) then
row[i]="TESTED"
end
end
table.insert(proxy.response.resultset.rows, row )
end
proxy.response.type = proxy.MYSQLD_PACKET_OK
return proxy.PROXY_SEND_RESULT
end

Corona reading and writing files (first time access)

I'm trying to write a function that will read sound and music states before starting my application.
The problem is: The first time it will run, there will be no data recorded.
First, I tried the suggested JSON function from here and I got this error:
Attempt to call global 'saveTable' (a nil value)
Is there a way to test if the file exists?
Then, I tried this one:
-- THIS function is just to try to find the file.
-- Load Configurations
function doesFileExist( fname, path )
local results = false
local filePath = system.pathForFile( fname, path )
--filePath will be 'nil' if file doesn,t exist and the path is "system.ResourceDirectory"
if ( filePath ) then
filePath = io.open( filePath, "r" )
end
if ( filePath ) then
print( "File found: " .. fname )
--clean up file handles
filePath:close()
results = true
else
print( "File does not exist: " .. fname )
end
return results
end
local fexist= doesFileExist("optionsTable.json","")
if (fexist == false) then
print (" optionsTable = nil")
optionsTable = {}
optionsTable.soundOn = true
optionsTable.musicOn = true
saveTable(optionsTable, "optionsTable.json") <<<--- ERROR HERE
print (" optionsTable Created")
end
The weird thing is that I'm getting an error at the saveTable(optionsTable,"optionsTable.json"). I just can't understand why.
If you have a working peace of code that handles the first time situation it will be enough to me. Thanks.
here's some code to check if the file exist you have to try and open the file first to know if it exist
function fileExists(fileName, base)
assert(fileName, "fileName is missing")
local base = base or system.ResourceDirectory
local filePath = system.pathForFile( fileName, base )
local exists = false
if (filePath) then -- file may exist wont know until you open it
local fileHandle = io.open( filePath, "r" )
if (fileHandle) then -- nil if no file found
exists = true
io.close(fileHandle)
end
end
return(exists)
end
and for usage
if fileExists("myGame.lua") then
-- do something wonderful
end
you can refer to this link for details