Alexander Gladysh
Alexander Gladysh

Reputation: 41443

Lua source code transformation: strip away a function call given its approximate location

I have a bunch of Lua 5.1 files that have this (or similar) construct inside:

...
local alpha, 
      beta
      = FUNCTION "gamma"
      {
        'delta',
        'epsilon'
      }
...

That is, a call to the FUNCTION that returns a function that returns some values, that are assigned to some local variables, declared at the spot.

Exact code may vary somewhat — both in style (say, everything may be on the same line), and in content (say, there can be zero arguments passed to the second function call). But always things start with local, and end with two chained function calls.

I've got a line number for FUNCTION call. I need to find the end of the last function call in the chain (this particular one; there can be more FUNCTION calls somewhere further in the file), and remove all file content upwards from that point.

I.e., before:

print("foo") -- 01
local alpha = FUNCTION 'beta' -- 02
{ "gamma" } -- 03
print("bar") -- 04

after:

 -- 03
print("bar") -- 04

Any clues on how to approach this?


Update:

To be more clear on why naïve regexp approach wouldn't work, a real life example:

local alpha
      = FUNCTION ( -- my line number points here
          FUNCTION 'beta' { 'gamma' } ()
            and 'epsilon'
             or 'zeta'
        )
      {
        'eta'
      } -- should be cut from here and above as a result

Upvotes: 0

Views: 220

Answers (1)

Egor Skriptunoff
Egor Skriptunoff

Reputation: 23757

Main idea: search for adjacent tits ()() !

local line_no = 12
local str = [[
print("foo")
local nif_nif
      = FUNCTION (
          FUNCTION 'beta' { 'gamma' } ()
            and 'epsilon'
             or 'zeta'
        )
      {
        'eta'
      }
local nuf_nuf
      = FUNCTION (      -- this is line#12
          FUNCTION 'beta' { 'gamma' } ()
            and 'epsilon'
             or 'zeta'
        )
      {
        'eta'
      }                 -- should be cut from here
local naf_naf
      = FUNCTION (
          FUNCTION 'beta' { 'gamma' } ()
            and 'epsilon'
             or 'zeta'
        )
      {
        'eta'
      }
print("bar")
]]

-- cut all text before target "local" keyword
str = str:gsub('\n','\0',line_no):gsub('^.*(local.-%z)','%1'):gsub('%z','\n')

-- enclose string literals and table constructors into temporary parentheses
str = str:gsub('%b""','(\0%0\0)')
         :gsub("%b''",'(\0%0\0)')
         :gsub("%b{}",'(\0%0\0)')

-- replace text in parentheses with links to it
local n, t = 0, {}
str = str:gsub('%b()', function(s) n=n+1 t[n..'']=s return'<\0'..n..'>' end)

-- search for first chained function call and cut it out
str = str:gsub('^.-<%z%d+>%s*<%z%d+>', '')
repeat
  local ctr
  str, ctr = str:gsub('^%s*<%z%d+>', '')
until ctr == 0

-- replace links with original text
t, str = nil, str:gsub('<%z(%d+)>', t)

-- remove temporary parentheses
str = str:gsub('%(%z', ''):gsub('%z%)', '')

print(str)

Output:

                 -- should be cut from here
local naf_naf
      = FUNCTION (
          FUNCTION 'beta' { 'gamma' } ()
            and 'epsilon'
             or 'zeta'
        )
      {
        'eta'
      }
print("bar")

Upvotes: 1

Related Questions