Karai17
Karai17

Reputation: 943

Complicated String Substitute issue

I am trying to replace some text with some other text. Simple, right? Heh.

So first off, I have a string of text:

local str = "This is some >>1234 text. This is some >>>/other/1234 text."

You will note the funny looking formatting within the text. I need to replace those with HTML. But there is a catch: I need to access a database using the data within that formatting. I am using the following formats to grab data:

local pattern1 = ">>(%d+)"
local pattern2 = ">>>/(%w+)/(%d*)"

In both patterns, I need to use the %d section to check the database. The reason for this is that depending on the data, I need to format the link differently. Ultimately, I want to have something like this:

local formatted = "This is some <a href='website.com/a/1220#1234'>>>1234</a> text. This is some <a href='website.com/other/1156#1234'>>>>/other/1234</a> text."

So my first attempt at this was to run a function on each instance of a match in string.gsub. This caused C-yield errors because I was trying to access C junk (database driver) inside a coroutine, which is, as I learned, a no-no.

I'm not really sure what my second attempt should be. I'm thinking that I need to iterate over the text with string.match to pull out the indices of the places I want to grab, then use gsub to get the data I need to access the database and store that somewhere, then rebuild the string from scratch.

Is that correct, or is there a better way?

Error

attempt to yield across C-call boundary
Traceback

stack traceback:
    [C]: in function 'receive'
    /home/karai/.luarocks/share/lua/5.1/pgmoon/init.lua:419: in function 'receive_message'
    /home/karai/.luarocks/share/lua/5.1/pgmoon/init.lua:233: in function 'query'
    /usr/share/lua/5.1/lapis/db/postgres.lua:64: in function 'select'
    ./src/text_formatter.lua:39: in function <./src/text_formatter.lua:24>
    [C]: in function 'gsub'
    ./src/text_formatter.lua:55: in function 'quote'
    ./src/board.lua:58: in function 'handler'
    /usr/share/lua/5.1/lapis/application.lua:399: in function 'resolve'
    /usr/share/lua/5.1/lapis/application.lua:408: in function </usr/share/lua/5.1/lapis/application.lua:406>
    [C]: in function 'xpcall'
    /usr/share/lua/5.1/lapis/application.lua:406: in function 'dispatch'
    /usr/share/lua/5.1/lapis/nginx.lua:205: in function 'serve'
    content_by_lua(nginx.conf.compiled:27):2: in function <content_by_lua(nginx.conf.compiled:27):1>

Upvotes: 2

Views: 71

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473222

It seems that the core of your problem is that you want to yield at each match (presumably until the database query is finished), then continue processing.

To do this, you should use gmatch first. It does what gsub does, but without the substitution; it returns an iterator that you can loop over that returns matches. So use this to generate your list of queries. This should be stored in an array:

local matches = {}
for match in my_string:gmatch(pattern) do
  matches[#matches + 1] = match
end

Then just walk the array, doing your database/yield stuff.

local repls = {}
for i, match in ipairs(matches) do
  --Do database query&yield stuff with `match`.
  repls[i] = --Generate the data you want to replace this match with.
end

After that, just do gsub to replace the string. The order of matches with gmatch and gsub are identical, so you don't need to care about the arguments:

local i = 0 --Not zero-based; keep reading
local fixed_string = my_string:gsub(pattern,
  function() --Don't care about the arguments.
    i = i + 1
    return repls[i] --Started at zero, so that this would be correct.
  end
)

Upvotes: 4

Related Questions