How to do async functions in Lua? - asynchronous

I have a database query in a function that return the result of this query and in another function I want to retrieve that result but I don't know how to do this async.
I'm using "mysqloo" library https://github.com/FredyH/MySQLOO for my database queries.
The first function, with the query :
function meta:getMoney()
local query2 = databaseObject:query("SELECT economy FROM luvinsastroi_player WHERE steamid = '" .. self:SteamID64() .. "' ")
query2.onData = function( q, d)
return tonumber(d['economy'])
end
query2:start()
end
then :
hook.Add( "PlayerSay", "MoneyCommand", function( ply, text, team )
if(text == "/money") then
local money = ply:getMoney()
ply:PrintMessage( HUD_PRINTTALK, "Vous avez " .. money .. "€." )
end
end )
In the second function, money is nil and so Error on ply:PrintMessage ( HUD_PRINTTALK, "Vous avez " .. money .. "€." ) attempt to concatenate a nil value (money)
How to wait return tonumber(d['economy']) from the meta:getMoney() function ?

This might work, but I'm not sure:
function meta:getMoney(cb)
local query2 = databaseObject:query("SELECT economy FROM luvinsastroi_player WHERE steamid = '" .. self:SteamID64() .. "' ")
if cb then
query2.onData = function(q, d)
cb(tonumber(d['economy']))
end
else
query2.onData = function(q, d)
return tonumber(d['economy'])
end
end
query2:start()
end
hook.Add("PlayerSay", "MoneyCommand", function(ply, text, team)
if(text == "/money") then
local function callback_money(money)
ply:PrintMessage(HUD_PRINTTALK, "Vous avez " .. money .. "€." )
end
ply:getMoney(callback_money)
end
end)

Related

Range check and Length check in Ada (SPARK Mode)

these last weeks I have been trying to learn the ADA language, to do it I made an exercise to reverse a string using recursion, however when I compile it with GNATProve it gives me several errors which I have not been able to solve, it would be of great help if you could guide me on how to solve them using Preconditions and Postconditions.
My code:
function String_Reverse(Str:String) return String with
Pre => Str'Length > 0 ,
Post => String_Reverse'Result'Length <= Str'Length;
function String_Reverse (Str : String) return String is
Result : String (Str'Range);
begin
if Str'Length = 1 then
Result := Str;
else
Result :=
String_Reverse (Str (Str'First + 1 .. Str'Last)) &
Str (Str'First);
end if;
return Result;
end String_Reverse;
Errors:
dth113.adb:18:69: low: range check might fail
18>| String_Reverse (Str (Str'First + 1 .. Str'Last)) &
19 | Str (Str'First);
reason for check: result of concatenation must fit in the target type of the assignment
possible fix: precondition of subprogram at line 8 should mention Str
8 | function String_Reverse(Str:String) return String with
| ^ here
dth113.adb:18:69: medium: length check might fail
18>| String_Reverse (Str (Str'First + 1 .. Str'Last)) &
19 | Str (Str'First);
reason for check: array must be of the appropriate length
possible fix: precondition of subprogram at line 8 should mention Str
8 | function String_Reverse(Str:String) return String with
| ^ here
I'm tried using Preconditons and Postconditions about the input Str length
Gnatprove appears to have some difficulty with concatenating arrays.
This below proves, using subtypes rather than pre- and post-conditions. Proving that the result is actually the reverse of the input string might be trickier!
Spec:
pragma SPARK_Mode;
function String_Reverse (Str : String) return String with
Pre => Str'Length > 0,
Post => String_Reverse'Result'Length = Str'Length;
Body:
pragma SPARK_Mode;
function String_Reverse (Str : String) return String is
subtype Result_String is String (Str'Range);
Result : Result_String;
begin
if Str'Length = 1 then
Result := Str;
else
declare
subtype Part_String is String (1 .. Str'Length - 1);
Reversed_Part : constant Part_String
:= String_Reverse (Str (Str'First + 1 .. Str'Last));
begin
Result := Reversed_Part & Str (Str'First);
end;
end if;
return Result;
end String_Reverse;
With a minor change you could handle zero-length strings as well.
Your program keeps appending the bulk of the string to your result. This makes a string much larger than the initial string parameter. You cannot fix this with improved preconditions or post conditions.
See the difference between your solution and the Reverse_String function shown below. In the solution below the size of the returned string cannot be the size of the function string in parameter because it is building the reversed string with each recursion and the size of the string cannot be determined until all recursions complete.
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
function Reverse_String(S : in String; Idx : Positive) return String is
begin
if Idx < S'Last then
return Reverse_String(S, Idx + 1) & S(Idx);
else
return S(Idx) & "";
end if;
end Reverse_String;
S1 : String := "Hello World";
s2 : String := "1234567890";
N1 : Integer := 12345;
N3 : Integer;
begin
Put_Line(S1 & " : " & Reverse_String(S1, 1));
Put_Line(S2 & " : " & Reverse_String(S2, 1));
N3 := Integer'Value(Reverse_String(N1'Image, 1));
Put_Line(N1'Image & " : " & N3'Image);
end Main;
This version of Reverse_String keeps incrementing the index of the input string and concatenating that character at the end of all the characters yet to reverse.
The Reverse_String's else clause appends an empty string to the last character so that it is viewed by the compiler as a string and not a character because S(Idx) is a single character and is not a string, but concatenating S(Idx) with an empty string results in a string.
Here is another implementation
function String_Reverse (Str : String) return String is
Result : String (Str'Range) := Str;
begin
for I in reverse Str'Range loop
Result(Str'Last - I + Result'First) := Str(I);
end loop;
return Result;
end String_Reverse;
SPARK seems to be happier with expression functions than with regular functions in many cases. I don't know if it will make a difference, but you could try rewriting the body of your function as
function String_Reverse (Str : String) return String is
(if Str'Length = 1 then
Str
else
String_Reverse (Str (Str'First + 1 .. Str'Last) ) & Str (Str'First) );
(I hope I've got the parentheses balanced.)
Typically one allows null strings, and the function becomes something like
function Reversed (S : in String) return String with
Post => Reversed'Result'Length = S'Length;
function Reversed (S : in String) return String is
(if S'Length = 0 then ""
else Reversed (S (S'first + 1 .. S'Last) & S (S'First) );
(Hint: "Ada" is a woman's name, not an acronym. GNATProve is a static-analysis tool, not a compiler.)

Error map-functions.lua:42: bad argument #2 to 'draw' (Quad expected, got nil) , any idea what's wrong?

I'm very new to love and lua and I don't know why I keep getting this message, any ideas? here's my code, hope you guys know what I could do.
in maps-functions.lua
function loadMap(path)
love.filesystem.load(path)()
end
function newMap(tileW, tileH, tilesetPath, tileString, quadInfo)
TileW = tileW
TileH = tileH
Tileset = love.graphics.newImage(tilesetPath)
local tilesetW, tilesetH = Tileset:getWidth(), Tileset:getHeight()
Quads = {}
for _,info in ipairs(quadInfo) do
-- info[1] = the character, info[2] = x, info[3] = y
Quads[info[1]] = love.graphics.newQuad(info[2], info[3], TileW, TileH, tilesetW, tilesetH)
end
TileTable = {}
local width = #(tileString:match("[^\n]+"))
for x = 1,width,1 do TileTable[x] = {} end
local x,y = 1,1
for row in tileString:gmatch("[^\n]+") do
assert(#row == width, 'Map is not aligned: width of row ' .. tostring(y) .. ' should be ' .. tostring(width) .. ', but it is ' .. tostring(#row))
x = 1
for tile in row:gmatch(".") do
TileTable[x][y] = tile
x = x + 1
end
y=y+1
end
end
function drawMap()
for x,column in ipairs(TileTable) do
for y,char in ipairs(column) do
love.graphics.draw(Tileset, Quads[ char ] , (x-1)*TileW, (y-1)*TileH)
end
end
end
Perhaps it has something to do with the map I drew or the image?

What's the best way to parse an SQL fragment string into a List(of string) for a Listbox control?

I'm trying to take this string:
(("DISPLAY_NAME" like N'sadf%') And ("ID" = 2) And ("IsCRITERION" = null))
and parse it into a List(of string) so that it can be displayed like:
(
(
"DISPLAY_NAME" like N'sadf%'
)
And
(
"ID" = 2
)
Or
(
"IsCRITERION" = null
)
)
I'm close but don't quite have it. My code currently looks like:
Dim filterlist As New List(Of String)
Dim temp As String = String.Empty
Dim lvl As Integer = 0
Dim pad As String = String.Empty
For Each chr As Char In originalString '--- filter is the string i posted above
Select Case chr.ToString.ToLower()
Case "("
filterlist.Add(pad.PadLeft(lvl * 5) & chr)
lvl += 1
Case ")"
filterlist.Add(pad.PadLeft(lvl * 5) & temp)
If lvl > 0 Then lvl -= 1
filterlist.Add(pad.PadLeft(lvl * 5) & chr)
'If lvl > 0 Then lvl -= 1
temp = String.Empty
Case Else
temp &= chr
End Select
Next
'--- Removes the empty line produced by generating the List(of String)
filterlist = filterlist.Where(Function(s) Not String.IsNullOrWhiteSpace(s)).ToList()
listSelectedCriteria.DataSource = filterlist
listSelectedCriteria.DataBind()
Unfortunately, the above code produces something close to what I desire but the "And"s and "Or"s are not in the right places:
(
(
"DISPLAY_NAME" like N'sadf%'
)
(
And "ID" = 2
)
(
Or "IsCRITERION" = null
)
)
Would using regular expressions be better? Thanks for the help
Probably the "best" way (although that's getting into "primarily opinion-based" territory) would be to use a parser, but assuming that your input is limited to similar looking strings, here's what I came up with:
Dim originalString = "((""DISPLAY_NAME"" like N'sadf%') And (""ID"" = 2) And (""IsCRITERION"" = null))"
Dim filterlist = New List(Of String)()
Dim temp = New StringBuilder()
Dim lvl = 0
Dim addLine =
Sub(x As String)
filterlist.Add(New String(" ", lvl * 4) & x.Trim())
End Sub
For Each c In originalString
Select Case c
Case "("
If temp.Length > 0 Then
addLine(temp.ToString())
temp.Clear()
End If
addLine("(")
lvl += 1
Case ")"
If temp.Length > 0 Then
addLine(temp.ToString())
temp.Clear()
End If
lvl -= 1
addLine(")")
Case Else
temp.Append(c)
End Select
Next
If temp.Length > 0 Then
addLine(temp.ToString())
temp.Clear()
End If
filterlist.Dump() ' LINQPad ONLY
This results in:
(
(
"DISPLAY_NAME" like N'sadf%'
)
And
(
"ID" = 2
)
And
(
"IsCRITERION" = null
)
)
However, you will probably end up having to add code as you find different inputs that don't quite work how you want.
Instead of looking at each characters, I would start be doing a split. And then add/remove padding depending on what character is at the start.
Dim tempString As String = "((""DISPLAY_NAME"" like N'sadf%') And (""ID"" = 2) And (""IsCRITERION"" = null))"
Dim curPadding As String = ""
Const padding As String = " "
Dim result As New List(Of String)
For Each s As String In Text.RegularExpressions.Regex.Split(tempString, "(?=[\(\)])")
If s <> "" Then
If s.StartsWith("(") Then
result.Add(curPadding & "(")
curPadding &= padding
result.Add(curPadding & s.Substring(1).Trim())
ElseIf s.StartsWith(")") Then
curPadding = curPadding.Substring(padding.Length)
result.Add(curPadding & ")")
result.Add(curPadding & s.Substring(1).Trim())
Else
result.Add(curPadding & s)
End If
End If
Next

How to print a sqlite table content with genie programming language

Based on previous questions here I managed to create the dataset, print all recipes listed and now I am trying to pick one of the recipes from that list and show its Title, Instructions and Ingredients. The instructions are mapped to the Recipes via the pkID column and the ingredients are mapped to the Recipes through a recipeID column. When I open the database on Sqlite Database Browser I can access this information inside the Tables dropdown list, so I suppose the proper name for them are tables within the database.
I am not being able to "filter" by pkID and by recipeID, so that after picking one recipe, only the appropriate content is shown.
This is the code in Python of what I am trying to do in Genie:
def PrintSingleRecipe(self,which):
sql = 'SELECT * FROM Recipes WHERE pkID = %s' % str(which)
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
for x in cursor.execute(sql):
recipeid =x[0]
print "Title: " + x[1]
print "Serves: " + x[2]
print "Source: " + x[3]
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
sql = 'SELECT * FROM Ingredients WHERE RecipeID = %s' % recipeid
print 'Ingredient List:'
for x in cursor.execute(sql):
print x[1]
print ''
print 'Instructions:'
sql = 'SELECT * FROM Instructions WHERE RecipeID = %s' % recipeid
for x in cursor.execute(sql):
print x[1]
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
resp = raw_input('Press A Key -> ')
I have not been able to improve much of my code, it seems that using the approach I used before of iterating in a step statement cannot be used here. This is how far I got in Genie:
def PrintSingleRecipe(db:Database)
stmt:Statement = PreparedStatements.select_all( db )
res:int = UserInterface.raw_input("Select a recipe -> ").to_int()
cols:int = stmt.column_count ()
var row = new dict of string, string
item:int = 1
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
while res == ROW
for i:int = 0 to (cols - 1)
row[ stmt.column_name( i ) ] = stmt.column_text( i )
stdout.printf( "%-5s", item.to_string( "%03i" ))
stdout.printf( "%-30s", row[ "Title" ])
stdout.printf( "%-20s", row[ "Serves" ])
stdout.printf( "%-30s\n", row[ "Source" ])
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
print "Ingredient list"
print " "
stdout.printf("%-5s", item.to_string( "%03i" ))
I have found a solution to the problem, maybe it can be optimized. For now it is enough.
Answers from another question helped immensely. The solution I used was to use the exec function and point the callback to the PrintSingleRecipe().
Some adjustments had to be done for it to work as a callback, but I got what I needed.
Here is the code where the function gets called:
while true
response:string = UserInterface.get_input_from_menu()
if response == "1" // Show All Recipes
PrintAllRecipes(db)
else if response is "2" // Search for a recipe
pass
else if response is "3" //Show a Recipe
res:string = UserInterface.raw_input("Select a recipe -> ")
sql:string = "SELECT * FROM Recipes WHERE pkID = " + res
db.exec(sql, PrintSingleRecipe, null)
else if response is "4"//Delete a recipe
pass
else if response is "5" //Add a recipe
pass
else if response is "6" //Print a recipe
pass
else if response is "0" //Exit
print "Goodbye"
break
else
print "Unrecognized command. Try again."
Here is how the PrintSingleRecipe looks like:
def PrintSingleRecipe(n_columns:int, values:array of string, column_names:array of string):int
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
for i:int = 0 to n_columns
stdout.printf ("%s = %s\n", column_names[i], values[i])
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
print "Ingredient list"
print " "
return 0

Call function recursively without additional parameter

I wrote vbscript function which search the list of free ports on Unix. So my code looks like this:
Function FindFreePorts(ByVal intPortToStart, ByVal intCountOfPortToSearch, ByVal arrFreePorts)
Dim i, arrCommand, arrTmp, blnFindAllPort, j, strCommand
Dim blnFree, intCountOfFreePorts
strCommand = "lsof -i | awk '{print $8}' | sed -n '/"
For i = intPortToStart To intPortToStart+intCountOfPortToSearch - 1
If i = intPortToStart+intCountOfPortToSearch - 1 Then
strCommand = strCommand & ".*"& i & "$/s/.*://p'"
Else
strCommand = strCommand & ".*:" & i &"\|"
End If
Next
Push arrCommand, strCommand
arrTmp = Array()
arrTmp = ExecuteCommandOnUnix(arrCommand, Null, _
Environment.Value("UNIXHOST_NAME"), _
Environment.Value("UNIXHOST_USER"), _
Environment.Value("UNIXHOST_PWD"))
' Count of busy port is UBound(arrTmp) - the other ports are free
' Find free ports
intCountOfFreePorts = intCountOfPortToSearch
For i = intPortToStart To intPortToStart+intCountOfPortToSearch - 1
blnFree = True
For j = 0 To UBound(arrTmp)
If arrTmp(j) = CStr(i) Then
blnFree = False
j = UBound(arrTmp)
End If
Next
If blnFree Then
Push arrFreePorts, i
intCountOfFreePorts = intCountOfFreePorts - 1
End If
Next
'
If intCountOfFreePorts = 0 Then
blnFindAllPort = True
Else
blnFindAllPort = False
End If
If Not blnFindAllPort Then
' We found UBound(arrFreePorts), we need to find intCountOfPortToSearch - UBound(arrFreePorts)
Do While intCountOfPortToSearch - UBound(arrFreePorts) - 1 <> 0
arrFreePorts = FindFreePorts(intPortToStart + intCountOfPortToSearch + 1, intCountOfPortToSearch - UBound(arrFreePorts) - 1, arrFreePorts)
If intCountOfPortToSearch - UBound(arrFreePorts) - 1 = 0 Then
Exit Do
End If
Loop
End If
FindFreePorts = arrFreePorts
End Function
As you can see I call this function recursively in Do While cycle. Everything works ok but I don't like arrFreePorts parameter here. So I should write this code to execute my function:
arrPort = FindFreePorts(intStartFromPort, intCountToSearch, arrPort)
But I have no idea how to rewrite this function without this parameter. Then I could call it in more simple way:
arrPort = FindFreePorts(intStartFromPort, intCountToSearch)
Thanks in advance.
To keep things (experiments, illustration of #Bond's contribution) simple, let's consider the task of putting the chars of a string into an array.
A function that shall return an array (without getting it via parameter or global) needs to create the array:
Function f1(s) ' default ByRef is ok, because Left and Mid return new (sub)strings
If "" = s Then
f1 = Array() ' return empty array for empty string
Else
Dim t : t = f1(Mid(s, 2)) ' recurse for tail - sorry no tail recursion
f1 = cons(Left(s, 1), t) ' copy! result of cons to f1/function's return value
End If
End Function
The growing of the array could be done inline, but for clarity I use a helper function cons():
Function cons(e, a) ' default ByRef is ok; neither e nor a are changed
ReDim aTmp(UBound(a) + 1) ' create larger array
Dim i : i = 0
aTmp(i) = e ' 'new' head
For i = 1 To UBound(aTmp)
aTmp(i) = a(i - 1) ' copy 'old' elms
Next
cons = aTmp ' copy! aTmp to cons/function's return value
End Function
Calling the function is nice:
WScript.Echo 3, "[" & Join(f1("12456789"), ",") & "]"
The price to pay for avoiding the passing of 'the' array:
a lot of copies (array assingment copies in VBScript)
no tail recursion (I doubt, however, that VBScript exploits it)
about factor 10 slower than the Sub approach ((c) Bond) below
As in this case the recursive calls can/should work on the 'same' array, a Sub does the task more efficiently (and is less complex):
Sub s1(s, a) ' default ByRef is ok; a should be modified, s isn't touched
If "" <> s Then
ReDim Preserve a(UBound(a) + 1) ' grow! a
a(UBound(a)) = Left(s, 1)
s1 Mid(s, 2), a ' tail recursion for tail
End If
End Sub
Calling it looks nasty:
ReDim a(-1) : s1 "123456789", a : WScript.Echo 3, "[" & Join(a, ",") & "]"
But that can be avoided by a wrapper:
Function f2(s)
ReDim aTmp(-1)
s1 s, aTmp
f2 = aTmp
End Function

Resources