I need to convert a sip callId (eg. 1097074724_100640573#8.8,8.8) string into requestId and I am using sha1 digest to get a hash. I need to convert this hex-decimal into uint64_t due to internal compatibility:
--
-- Obtain request-id from callId
--
-- Returns hash
--
function common_get_request_id( callId )
local command = "echo -n \"" .. callId .. "\" | openssl sha1 | sed 's/(stdin)= //g'"
local handle = assert( io.popen( command, "r" ) )
local output = handle:read( "*all" )
local outputHash = string.gsub(output, "\n", "") -- strip newline
handle:close()
-- How to convert outputHash to uint64?
end
I am not sure about uint64 support in Lua. Also, how to do the conversion?
You can receive two 32-bit integer numbers from Lua as two "double" values and convert them to one "uint64" on C side.
outputHash = '317acf63c685455cfaaf1c3255eeefd6ca3c5571'
local p = 4241942993 -- some prime below 2^32
local c1, c2 = 0, 0
for a, b in outputHash:gmatch"(%x)(%x)" do
c1 = (c1 * 16 + tonumber(a, 16)) % p
c2 = (c2 * 16 + tonumber(b, 16)) % p
end
return c1, c2 -- both numbers 0..(2^32-1)
Related
How can i transform this following string:
string = "1,2,3,4"
into a table:
table = {1,2,3,4}
thanks for any help ;)
Let Lua do the hard work:
s="1,2,3,4"
t=load("return {"..s.."}")()
for k,v in ipairs(t) do print(k,v) end
Below is the code adapted from the Scribunto extension to MediaWiki. It allows to split strings on patterns that can be longer than one character.
-- Iterator factory
function string:gsplit (pattern, plain)
local s, l = 1, self:len()
return function ()
if s then
local e, n = self:find (pattern, s, plain)
local ret
if not e then
ret = self:sub (s)
s = nil
elseif n < e then
-- Empty separator!
ret = self:sub (s, e)
if e < l then
s = e + 1
else
s = nil
end
else
ret = e > s and self:sub (s, e - 1) or ''
s = n + 1
end
return ret
end
end, nil, nil
end
-- Split function that returns a table:
function string:split (pattern, plain)
local ret = {}
for m in self:gsplit (pattern, plain) do
ret [#ret + 1] = m
end
return ret
end
-- Test:
local str = '1,2, 3,4'
print ('table {' .. table.concat (str:split '%s*,%s*', '; ') .. '}')
You can use gmatch and the pattern (%d+) to create an iterator and then populate the table.
local input = "1,2,3,4"
local output = {}
for v in input:gmatch("(%d+)") do
table.insert(output, tonumber(v))
end
for _,v in pairs(output) do
print(v)
end
The pattern (%d+) will capture any number of digits(0-9).
This is a narrow solution, it does not handle blank entries such as
input = "1,2,,4"
output = {1,2,4}
It also does not care what the delimitator is, or if it is even consistent for each entry.
input = "1,2 3,4"
output = {1,2,3,4}
Reference:
Lua 5.3 Manual, Section 6.4 – String Manipulation: string.gmatch
FHUG: Understanding Lua Patterns
We had given below task
Create three function using Lua which can perform addition, subtraction and multiplication/
The program should be
Accept any number.
Check the operator + - *. If not the print "Invalid operand" and exit.
Accept two operands. if any operand not number print "Invalid operand" and exit. use - tonumber()
Perform the operation and print result.
We had written code as below which not working as expected , not sure where we went wrong, please guide us
local operators = {
["+"] = function(x,y) return x+y end,
["-"] = function(x,y) return x-y end,
["*"] = function(x,y) return x*y end
}
local function default()
print "Invalid operand"
end
local num1 = io.read()
local num2 = io.read()
local operator = io.read()
local func (
if operator == "+" then
local add = tonumber(num1) + tonumber(num2)
end
if operator == "-" then
local subtract = tonumber(num1) - tonumber(num2)
end
if operator == "*" then
local multiply = tonumber(num1) * tonumber(num2)
end
)
or default
print(func(num1,num2))
io.read()
Correct code is
local operators = {
["+"] = function (x, y) return x + y end,
["-"] = function (x, y) return x - y end,
["*"] = function (x, y) return x * y end,
}
local operator = operators[io.read()]
local num1 = tonumber(io.read())
local num2 = tonumber(io.read())
if num1 and num2 then
if operator then
print(operator(num1, num2))
else
print("Invalid operator")
end
else
print("Invalid operand")
end
Your code has syntax errors and doesn't make too much sense.
local func (
if operator == "+" then
local add = tonumber(num1) + tonumber(num2)
end
if operator == "-" then
local subtract = tonumber(num1) - tonumber(num2)
end
if operator == "*" then
local multiply = tonumber(num1) * tonumber(num2)
end
)
or default
To define a local function you can write
local myFunction = function() end
or
local function myFunction() end
But not
local func()
As you define the function it can never be nil. So short-circuiting a function definition like
local default = function() end
local myFunction = function() end or default
doesn't make sense.
You should add instructions so the user knows what to enter befor you call io.read().
This part of your code is not used at all:
local operators = {
["+"] = function(x,y) return x+y end,
["-"] = function(x,y) return x-y end,
["*"] = function(x,y) return x*y end
}
Also your function func does nothing but creating local variables which are not used.
The function does not return a value so printing it's return value like print(func(num1, num2)) won't print a result as intended.
You also fail to check if the user actually entered valid characters like numbers or operators. This will cause Lua errors if the user inputs something else.
Make use of the operators table. Instead of setting operator to the string that a user inputs, set it to the function that the user needs. If the user inputs an operator that is not in the table, operator will be set to nil:
local operator = operators[io.read()]
Similarly, set num1 and num2 directly to the numbers that will be operands instead of setting them to user input strings:
local num1 = tonumber(io.read())
local num2 = tonumber(io.read())
If tonumber can't convert the user inputs into numbers, num1 or num2 will be set to nil.
You can use the fact that invalid inputs are given nil values to validate the input. You don't really need the default() function to simply report that input is bad, and I did not use this in the final code below. If you did want to remove this reporting to functions you should probably at least distinguish between bad operands and bad operators:
local function bad_operand()
print("Invalid operand")
end
local function bad_operator()
print("Invalid operator")
end
Now you just need to check whether the arguments are valid, and whether the operator is valid, and if so perform the calculation. Here is the code:
local operators = {
["+"] = function (x, y) return x + y end,
["-"] = function (x, y) return x - y end,
["*"] = function (x, y) return x * y end,
}
local num1 = tonumber(io.read())
local num2 = tonumber(io.read())
local operator = operators[io.read()]
if num1 and num2 then
if operator then
print(operator(num1, num2))
else
print("Invalid operator")
end
else
print("Invalid operand")
end
Here are some sample runs:
~/code/lua/scratch $ lua calc.lua
3
4
-
-1
~/code/lua/scratch $ lua calc.lua
7
5
*
35
~/code/lua/scratch $ lua calc.lua
3
/
/
Invalid operand
~/code/lua/scratch $ lua calc.lua
3
4
/
Invalid operator
The nice thing about using the operators table is that we can now add operators to the table to extend the functionality of the program without making any other changes:
local operators = {
["+"] = function (x, y) return x + y end,
["-"] = function (x, y) return x - y end,
["*"] = function (x, y) return x * y end,
["/"] = function (x, y) return x / y end,
["//"] = function (x, y) return x // y end,
}
Testing the new operators:
~/code/lua/scratch $ lua calc.lua
3
4
/
0.75
~/code/lua/scratch $ lua calc.lua
7
3
//
2
So I have a string that every 4 characters I need to insert a value like so.
local string = "24029400001000000000000000000000"
--insert : every 4 chars
--output
--2402:9400:0010:0000:0000:0000:0000:0000
The output i am looking for is 2402:9400:0010:0000:0000:0000:0000:0000
function string.chunk( str, n )
local k, t
t= { }
for k in str:gmatch( string.rep( ".", n ) ) do
table.insert( t, k )
end
return t
end
x = "24029400001000000000000000000000"
x_new = ""
for k, v in ipairs( x:chunk( 4 ) ) do
v = v .. ":"
x_new = x_new .. v
end
print(x_new)
--problem is 2402:9400:0010:0000:0000:0000:0000:0000: the : on the end of string
Possible solution:
<script src="https://github.com/fengari-lua/fengari-web/releases/download/v0.1.4/fengari-web.js"></script>
<script type="application/lua">
local s = "24029400001000000000000000000000"
s = s:gsub('....','%1:'):gsub(':$','')
print(s)
</script>
Yesterday I've wrote interpreter for my new programming language, and I've put it on Github. Before this i made some simple tests and it seemed to work but now when i wanted to write some examples - it didn't work at all! I was really surprised, how did I get that output. For simple programs like this:
string Hello World!
locate 0
puts
It worked really well, displaying output 'Hello World!'. More complicated program looked like this:
ADD 49
PUTCH
SUB 1
PUTCH
MUL 2
ADD 1
PUTCH
SUB 1
DIV 2
PUTCH
LOCATE 2
PUTD
LOCATE 0
SETV 10
PUTCH
SETV 0
LOCATE 0
STRING Hello World
LOCATE 0
PUTS
SETV 10
UNTILZERO
{
SUB 1
}
IFCC 10 == 10 {
LOCATE 0
SETV 0
STRING Ok!
LOCATE 0
PUTS
}
To understand this you need just some knowledge of Lua, C and Assembly.
How to run code above: ./lua start.lua test.txt -gencode
Output:
}UTSTE 0k! 10 {rld
local tape = {}
local pointer = 0
}UTSTE 0k! 10 {rldpe[pointer] + 49
(I don't understand any part of this invaild output)
So actually my code looks like this:
--[[
____ _ __ _ _ _
| _ \ (_) / _| | | _| || |_
| |_) |_ __ __ _ _ _ __ | |_ _ _ ___| | _|_ __ _|
| _ <| '__/ _` | | '_ \| _| | | |/ __| |/ /_| || |_
| |_) | | | (_| | | | | | | | |_| | (__| <|_ __ _|
|____/|_| \__,_|_|_| |_|_| \__,_|\___|_|\_\ |_||_|
Brainfuck# v 1.0. Copyright (C) by Krzysztof Szewczyk.
For more information check CONTRIB.MD and LICENSE.
Code is licensed under GPLv3.
--]]
output = "local tape = {}\nlocal pointer = 0\n" --We will use Lua 'eval'-like function.
brackets = 0
-- Parse command with parameters 'params', and parameter number
-- 'paramno'.
function parse(command,params,paramno)
if command == nil or params == nil or paramno == nil then return end
local cmd = string.upper(command)
if cmd == ";" then end
if cmd == "ADD" then
local amount = params[1]
output = output .. "tape[pointer] = tape[pointer] + " .. amount .. "\n"
end
if cmd == "SUB" then
local amount = params[1]
output = output .. "tape[pointer] = tape[pointer] - " .. amount .. "\n"
end
if cmd == "MUL" then
local amount = params[1]
output = output .. "tape[pointer] = tape[pointer] * " .. amount .. "\n"
end
if cmd == "DIV" then
local amount = params[1]
if amount == 0 then
print("[JIT] - Divide-by-zero error.")
end
output = output .. "tape[pointer] = tape[pointer] / " .. amount .. "\n"
end
if cmd == "ADDP" then
local amount = params[1]
output = output .. "pointer = pointer + " .. amount .. "\n"
end
if cmd == "SUBP" then
local amount = params[1]
output = output .. "pointer = pointer - " .. amount .. "\n"
end
if cmd == "MULP" then
local amount = params[1]
output = output .. "pointer = pointer * " .. amount .. "\n"
end
if cmd == "DIVP" then
local amount = params[1]
if amount == 0 then
print("[JIT] - Divide-by-zero error.")
end
output = output .. "pointer = pointer / " .. amount .. "\n"
end
if cmd == "LOCATE" then
local pos = params[1]
output = output .. "pointer = " .. pos .. "\n"
end
if cmd == "SETV" then
local val = params[1]
output = output .. "tape[pointer] = " .. val .. "\n"
end
if cmd == "STRING" then
local str = params[1]
for i = 1, #str do
local c = str:sub(i,i)
output = output .. "tape[pointer] = string.byte(\"" .. c .. "\")\n"
output = output .. "pointer = pointer + 1\n"
end
output = output .. "tape[pointer] = 0\n" --Remember to add null terminator (this can overwrite some of
--your crap stored in tape, so please have this in mind).
output = output .. "pointer = pointer + 1\n"
end
if cmd == "PUTCH" then
--Simply, no arguments
output = output .. "io.write(string.char(tape[pointer]))\n"
end
if cmd == "PUTD" then
--Simply, no arguments ^
output = output .. "io.write(tape[pointer])\n" --Just print integer (as integer, not character, for character see |)
end
if cmd == "PUTS" then
output = output .. "lastpntr=0\nwhile true do\nif tape[pointer] == 0 then break end\nio.write(string.char(tape[pointer]))\npointer = pointer + 1\nend\npointer=lastpntr\n" --I belive it's too complicated
--But it works.
end
if cmd == "GETCH" then
output = output .. "tape[pointer] = io.read()\n" --HACK: Any raw input is not possible in multiplatform way.
end
if cmd == "UNTILZERO" then
output = output .. "while tape[pointer]\n"
end
if cmd == "{" then
output = output .. "do\n"
brackets = brackets + 1;
end
if cmd == "}" then
output = output .. "end\n"
brackets = brackets - 1;
end
if cmd == "IUNTIL" then
local type = params[1]
local value = params[2]
output = output .. "while tape[pointer] " .. type .. value .. "\n"
brackets = brackets - 1;
end
if cmd == "TUNTIL" then
local type = params[1]
local value = params[2]
output = output .. "while tape[pointer] " .. type .. "tape[" .. value .. "]" .. "\n"
brackets = brackets - 1;
end
if cmd == "IFCC" then
local val1 = params[1];
local comp = params[2];
local val2 = params[3];
if params[4] == "{" then
output = output .. "if " .. val1 .. comp .. val2 .. " then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
if cmd == "IFCT" then
local val1 = params[1];
local comp = params[2];
local val2 = params[3];
if params[4] == "{" then
output = output .. "if " .. val1 .. comp .. "tape[" .. val2 .. "] then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
if cmd == "IFTT" then
local val1 = params[1];
local comp = params[2];
local val2 = params[3];
if params[4] == "{" then
output = output .. "if tape[" .. val1 .. "]" .. comp .. "tape[" .. val2 .. "] then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
end
-- Function to split strings. Any questions?
function string:split( inSplitPattern, outResults )
if not outResults then
outResults = { }
end
local theStart = 1
local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
while theSplitStart do
table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
theStart = theSplitEnd + 1
theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
end
table.insert( outResults, string.sub( self, theStart ) )
return outResults
end
-- I create new function to ensure that variables
-- won't escape local context.
function main(filename,gencodeswitch)
local input = io.open(filename, "r")
if input then
--No error found while opening file
while true do
--First, read line.
local line = input:read()
--Now, let's check is it nil.
--If so, we can break out of this loop.
if line == nil then break end
--Else, we need to parse this instruction.
--So break it into main command and it's params.
local space = string.find(line, " ") --Find first space occurence (to divide
--command from it's arguments).
local params = string.sub(line, space+1) -- To get params just split string.
local command = string.sub(line, 0, space-1) -- To get command without trailing space.
--Actually, this space will get removed.
local paramTable = params:split(",")
local paramAmount = 0
-- HACK: Looks like ineffective solution, but who cares?
for i = 1, #paramTable do
paramAmount = paramAmount + 1;
end
print ("line:" .. line)
parse(command,paramTable,paramAmount)
end
--Done parsing. Generate code
if brackets ~= 0 then print("[JIT] Unbalanced brackets.") os.exit() end
if gencodeswitch == "-gencode" then print(output) end
--loadstring(output)()
else
--Oops, an error occured. Couldn't open file.
print("[JIT]: Please pass vaild filename.")
os.exit() --Bye, see ya later
end
end
if arg[1] == nil then
--User didn't pass any arguments.
print("Brainfuck# v 1.0")
print("Ussage:")
print(".\lua start.lua <input> [-print]")
print("Where:")
print(".\lua - lua executable")
print("start.lua - main module name");
print("<input> - input filename (non-optional!)")
print("[-print] - Optional, print source before execution.")
os.exit();
else
--User passed an argument
main(arg[1],arg[2])
end
I can't find any errors but there must be a mistake.
If you want to see repo, here it is, but it doesn't contain
anything except license, readme, build and test scripts.
I've put some comments to make understanding this broken code
and trying to fixing it easier.
Can anyone point me out where have I made a mistake?
I took the liberty to completely revise your code as I saw too many problems with it. (It didn't even compile as you posted it.) I guess you're new to programming. Anyway, there is still much more work needed but I'll leave the rest of it for you. However, it does seem to work now for your example input at least.
I saved your example program as sample.bf and with the command
brainfuck#.lua sample.bf -gencode | lua got Hello World! as output. Which is correct, as far as I can tell.
--[[
____ _ __ _ _ _
| _ \ (_) / _| | | _| || |_
| |_) |_ __ __ _ _ _ __ | |_ _ _ ___| | _|_ __ _|
| _ <| '__/ _` | | '_ \| _| | | |/ __| |/ /_| || |_
| |_) | | | (_| | | | | | | | |_| | (__| <|_ __ _|
|____/|_| \__,_|_|_| |_|_| \__,_|\___|_|\_\ |_||_|
Brainfuck# v 1.0. Copyright (C) by Krzysztof Szewczyk.
For more information check CONTRIB.MD and LICENSE.
Code is licensed under GPLv3.
--]]
output = [[
local tape = {}
local pointer = 0
]] --We will use Lua 'eval'-like function.
brackets = 0
-- Parse command with parameters 'params', and parameter number 'paramno'.
function parse(cmd,params,paramno)
if cmd == nil or params == nil or paramno == nil then return end
cmd = cmd:upper()
local amount
if cmd == ";" then
elseif cmd == "ADD" then
amount = params[1]
output = output .. "tape[pointer] = tape[pointer] + " .. amount .. "\n"
elseif cmd == "SUB" then
amount = params[1]
output = output .. "tape[pointer] = tape[pointer] - " .. amount .. "\n"
elseif cmd == "MUL" then
amount = params[1]
output = output .. "tape[pointer] = tape[pointer] * " .. amount .. "\n"
elseif cmd == "DIV" then
amount = params[1]
if amount == 0 then
print("[JIT] - Divide-by-zero error.")
end
output = output .. "tape[pointer] = tape[pointer] / " .. amount .. "\n"
elseif cmd == "ADDP" then
amount = params[1]
output = output .. "pointer = pointer + " .. amount .. "\n"
elseif cmd == "SUBP" then
amount = params[1]
output = output .. "pointer = pointer - " .. amount .. "\n"
elseif cmd == "MULP" then
amount = params[1]
output = output .. "pointer = pointer * " .. amount .. "\n"
elseif cmd == "DIVP" then
amount = params[1]
if amount == 0 then
print("[JIT] - Divide-by-zero error.")
end
output = output .. "pointer = pointer / " .. amount .. "\n"
elseif cmd == "LOCATE" then
local pos = params[1]
output = output .. "pointer = " .. pos .. "\n"
elseif cmd == "SETV" then
local val = params[1]
output = output .. "tape[pointer] = " .. val .. "\n"
elseif cmd == "STRING" then
local s = params[1]
for i = 1, #s do
local c = s:sub(i,i)
output = output .. "tape[pointer] = string.byte(\"" .. c .. "\")\n"
output = output .. "pointer = pointer + 1\n"
end
output = output .. "tape[pointer] = 0\n"
--Remember to add null terminator (this can overwrite some of your crap
--stored on tape, so please have this in mind).
output = output .. "pointer = pointer + 1\n"
elseif cmd == "PUTCH" then --Simply, no arguments
output = output .. "io.write(string.char(tape[pointer]))\n"
elseif cmd == "PUTD" then --Simply, no arguments ^
output = output .. "io.write(tape[pointer])\n" --Just print integer (as integer, not character, for character see |)
elseif cmd == "PUTS" then
output = output .. [[
lastpntr=0
while true do
if tape[pointer] == 0 then break end
io.write(string.char(tape[pointer]))
pointer = pointer + 1
end
pointer=lastpntr
]] --I believe it's too complicated but it works.
elseif cmd == "GETCH" then
output = output .. "tape[pointer] = io.read()\n" --HACK: Any raw input is not possible in multiplatform way.
elseif cmd == "UNTILZERO" then
output = output .. "while tape[pointer]\n"
elseif cmd == "{" then
output = output .. "do\n"
brackets = brackets + 1
elseif cmd == "}" then
output = output .. "end\n"
brackets = brackets - 1
elseif cmd == "IUNTIL" then
local type = params[1]
local value = params[2]
output = output .. "while tape[pointer] " .. type .. value .. "\n"
brackets = brackets - 1
elseif cmd == "TUNTIL" then
local type = params[1]
local value = params[2]
output = output .. "while tape[pointer] " .. type .. "tape[" .. value .. "]" .. "\n"
brackets = brackets - 1
elseif cmd == "IFCC" then
local val1 = params[1]
local comp = params[2]
local val2 = params[3]
if params[4] == "{" then
output = output .. "if " .. val1 .. comp .. val2 .. " then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
elseif cmd == "IFCT" then
local val1 = params[1]
local comp = params[2]
local val2 = params[3]
if params[4] == "{" then
output = output .. "if " .. val1 .. comp .. "tape[" .. val2 .. "] then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
elseif cmd == "IFTT" then
local val1 = params[1]
local comp = params[2]
local val2 = params[3]
if params[4] == "{" then
output = output .. "if tape[" .. val1 .. "]" .. comp .. "tape[" .. val2 .. "] then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
end
-- Function to split strings. Any questions?
function string:split( inSplitPattern, outResults )
outResults = outResults or {}
local theStart = 1
local theSplitStart, theSplitEnd = self:find(inSplitPattern, theStart)
while theSplitStart do
table.insert( outResults, self:sub(theStart, theSplitStart-1) )
theStart = theSplitEnd + 1
theSplitStart, theSplitEnd = self:find(inSplitPattern, theStart)
end
table.insert( outResults, self:sub(theStart) )
return outResults
end
-- I create new function to ensure that variables won't escape local context.
function main(filename,gencodeswitch)
local file = io.open(filename, "r")
if file == nil then
--Oops, an error occured. Couldn't open file.
print("[JIT]: Please pass valid filename.")
os.exit() --Bye, see ya later
return
end
--No error found while opening file
local space,params,command,paramTable,paramAmount
for line in file:lines() do
--We need to parse this instruction.
--So break it into main command and it's params.
space = (line..' '):find(" ") --Find first space occurence (to divide command from it's arguments).
params = line:sub(space+1) -- To get params just split string.
command = line:sub(1, space-1) -- To get command without trailing space.
--Actually, this space will get removed.
paramTable = params:split(',')
-- HACK: Looks like ineffective solution, but who cares?
--for i = 1, #paramTable do paramAmount = paramAmount + 1 end
paramAmount = #paramTable
print ('--' .. line)
parse(command,paramTable,paramAmount)
end
file:close()
--Done parsing. Generate code
if brackets ~= 0 then print("[JIT] Unbalanced brackets.") os.exit() end
if gencodeswitch == "-gencode" then print(output) end
--loadstring(output)()
end
if arg[1] == nil then
--User didn't pass any arguments.
print [[
Brainfuck# v 1.0
Usage:
.\lua start.lua <input> [-print]
Where:
.\lua - lua executable
start.lua - main module name
<input> - input filename (non-optional!)
[-print] - Optional, print source before execution.]]
os.exit()
else
--User passed an argument
main(arg[1],arg[2])
end
I need to iterate over some pairs of strings in a program that I am writing. Instead of putting the string pairs in a big table-of-tables, I am putting them all in a single string, because I think the end result is easier to read:
function two_column_data(data)
return data:gmatch('%s*([^%s]+)%s+([^%s]+)%s*\n')
end
for a, b in two_column_data [[
Hello world
Olá hugomg
]] do
print( a .. ", " .. b .. "!")
end
The output is what you would expect:
Hello, world!
Olá, hugomg!
However, as the name indicates, the two_column_data function only works if there are two exactly columns of data. How can I make it so it works on any number of columns?
for x in any_column_data [[
qwe
asd
]] do
print(x)
end
for x,y,z in any_column_data [[
qwe rty uio
asd dfg hjk
]] do
print(x,y,z)
end
I'm OK with using lpeg for this task if its necessary.
function any_column_data(data)
local f = data:gmatch'%S[^\r\n]+'
return
function()
local line = f()
if line then
local row, ctr = line:gsub('%s*(%S+)','%1 ')
return row:match(('(.-) '):rep(ctr))
end
end
end
Here is an lpeg re version
function re_column_data(subj)
local t, i = re.compile([[
record <- {| ({| [ %t]* field ([ %t]+ field)* |} (%nl / !.))* |}
field <- escaped / nonescaped
nonescaped <- { [^ %t"%nl]+ }
escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"']], { t = '\t' }):match(subj)
return function()
local ret
i, ret = next(t, i)
if i then
return unpack(ret)
end
end
end
It basicly is a redo of the CSV sample and supports quoted fields for some nice use-cases: values with spaces, empty values (""), multi-line values, etc.
for a, b, c in re_column_data([[
Hello world "test
test"
Olá "hug omg"
""]].."\tempty a") do
print( a .. ", " .. b .. "! " .. (c or ''))
end
local function any_column_data( str )
local pos = 0
return function()
local _, to, line = str:find("([^\n]+)\n", pos)
if line then
pos = to
local words = {}
line:gsub("[^%s]+", function( word )
table.insert(words, word)
end)
return table.unpack(words)
end
end
end
Outer loop returns lines, and inner loop returns words in line.
s = [[
qwe rty uio
asd dfg hjk
]]
for s in s:gmatch('(.-)\n') do
for s in s:gmatch('%w+') do
io.write(s,' ')
end
io.write('\n')
end