Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lua: Quoted arguments passed as one in function

I'm attempting to simplify a script, and my attempts are failing. I'm making a function that will pass the given arguments and turn them into an indexed table, but I want to be able to pass quoted and non-quoted alike and have the function recognize that quoted arguments are considered one value while also respecting non-quoted arguments.

For example:

makelist dog "brown mouse" cat tiger "colorful parrot"

should return an indexed table like the following:

list_table = {"dog", "brown mouse", "cat", "tiger", "colorful parrot"}

The code I have works for quoted, but it's messing up on the non-quoted, and on top of that, adds the quoted arguments a second time. Here's what I have:

function makelist(str)
  require 'tprint'
  local list_table = {}
  for word in string.gmatch(str, '%b""') do
    table.insert(list_table, word)
  end
  for word in string.gmatch(str, '[^%p](%a+)[^%p]') do
    table.insert(list_table, word)
  end
  tprint(list_table)
end

I'm not understanding why the omission of quotes is being ignored, and also is chopping off the first letter. That is, this is the output I receive from tprint (a function that prints a table out, not relevant to the code):

makelist('dog "brown mouse" cat tiger "colorful parrot"')
1=""brown mouse""
2=""colorful parrot""
3="og"
4="rown"
5="mouse"
6="cat"
7="tiger"
8="olorful"
9="parrot"

As you can see, 'd', 'b', and 'c' are missing. What fixes do I need to make so that I can get the following output instead?

1="brown mouse"
2="colorful parrot"
3="dog"
4="cat"
5="tiger"

Or better yet, have them retain the same order they were dictated as arguments, if that's possible at all.

like image 943
Josh Avatar asked Jun 19 '26 07:06

Josh


2 Answers

local function makelist(str)
  local t = {}
  for quoted, non_quoted in ('""'..str):gmatch'(%b"")([^"]*)' do
    table.insert(t, quoted ~= '""' and quoted:sub(2,-2) or nil)
    for word in non_quoted:gmatch'%S+' do
      table.insert(t, word)
    end
  end
  return t
end
like image 178
Egor Skriptunoff Avatar answered Jun 23 '26 06:06

Egor Skriptunoff


It may be easier to simply split on whitespaces and concatenate those elements that are inside quotes. Something like this may work (I added few more test cases):

function makelist(str)
  local params, quoted = {}, false
  for sep, word in str:gmatch("(%s*)(%S+)") do
    local word, oquote = word:gsub('^"', "") -- check opening quote
    local word, cquote = word:gsub('"$', "") -- check closing quote
    -- flip open/close quotes when inside quoted string
    if quoted then -- if already quoted, then concatenate
      params[#params] = params[#params]..sep..word
    else -- otherwise, add a new element to the list
      params[#params+1] = word
    end
    if quoted and word == "" then oquote, cquote = 0, oquote end
    quoted = (quoted or (oquote > 0)) and not (cquote > 0)
  end
  return params
end
local list = makelist([[
dog "brown mouse" cat tiger " colorful parrot " "quoted"
in"quoted "terminated by space " " space started" next "unbalanced
]])
for k, v in ipairs(list) do print(k, v) end

This prints the following list for me:

1   dog
2   brown mouse
3   cat
4   tiger
5    colorful parrot 
6   quoted
7   in"quoted
8   terminated by space 
9    space started
10  next
11  unbalanced
like image 33
Paul Kulchenko Avatar answered Jun 23 '26 06:06

Paul Kulchenko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!