0

I would like to parse all lua functions declarations from a lua file. For instance, let's assume I've got this code:

function foo(a, b, c)
    local d = a + b
    local e = b - c
    return d *e
end

function boo(person)
    if (person.age == 18) then
        print("Adult")
    else
        print("Kid")
    end

    if (person.money > 20000) then
        print("Rich")
    else
        print("poor")
    end
end

I would like to have this result:

Group[1]:
    local d = a + b
    local e = b - c
    return d *e

Group[2]:
    if (person.age == 18) then
        print("Adult")
    else
        print("Kid")
    end

    if (person.money > 20000) then
        print("Rich")
    else
        print("poor")
    end

Basically, I want the function bodies in other words everyting between function declaration and the last end. However, I've came up with this:

(?<=function)(.*?)(?=end)

Thanks for your answers.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
x84x4
  • 19
  • 4
  • 1
    This is very hard, if not impossible, because regex is usually not recursive. – ssc-hrep3 Feb 13 '17 at 19:08
  • If this language is indent based, it might be doable. If not, then you'd have to be able to handle nesting, ie. _if if else if end end end_ –  Feb 13 '17 at 19:12

2 Answers2

2

If all your function definitions begin and end at column 1, then this works:

L=[[

function foo(a, b, c)
    local d = a + b
    local e = b - c
    return d *e
end

function boo(person)
    if (person.age == 18) then
        print("Adult")
    else
        print("Kid")
    end

    if (person.money > 20000) then
        print("Rich")
    else
        print("poor")
    end
end
]]

for b in L:gmatch("\nfunction.-\n(.-)\nend") do
    print("------------------")
    print(b)
end

Note that you need a blank line at the top of your code to find the first function.

lhf
  • 70,581
  • 9
  • 108
  • 149
  • 2
    BTW, that blank line before the first `function` can be just added before running the `gmatch`. – Wiktor Stribiżew Feb 13 '17 at 19:25
  • Hmm ok but the problem is that I can't be sure about how the output will be. For instance, functions which are just below each other (no empty blank line). I also forgot to mention that I want java code. – x84x4 Feb 13 '17 at 20:17
  • 1
    @x84x4, the code above does not need a blank lime between functions, just that they begin and end at column 1. – lhf Feb 13 '17 at 23:08
  • To solve "newline before first function" problem: `for b in L:gmatch("%f[^%z\n]function.-\n(.-)\nend") do` – Egor Skriptunoff Feb 14 '17 at 15:51
0

Here are some functions that will output the function bodies of a file, so long as the function definitions start and end in the first column, and so long as the functions are defined using either [local] function ... or [local] fname = function .... There is also a provision for detecting single-line function definitions.

The function func_bodies() is an iterator that returns tables containing the lines of the function bodies. The is_func_def() function returns nil if a line does not begin a function definition. The show_funcs() function uses the iterator and prints the results. Note that there is no need for blank lines before or between function definitions.

function is_func_def (line)
   return string.match(line, "^function%s+") or
          string.match(line, "^local%s+function%s+") or
          string.match(line, "^local%s+[%w_]+%s*=%s*function%s+") or
          string.match(line, "^[%w_]+%s*=%s*function%s+")
end

function func_bodies (file)
   local body
   local in_body = false
   local counter = 0
   local lines = io.lines(file)
   return function ()
      for line in lines do
         if in_body then
            if string.match(line, "^end") then
               in_body = false
               return counter, body
            else
               table.insert(body, line)
            end
         elseif is_func_def(line) then
            counter = counter + 1
            body = {}
            if string.match(line, "end$") then
               table.insert(body, string.match(line, "%)%s+(.+)%s+end$"))
               return counter, body
            else
               in_body = true
            end
         end
      end
      return nil
   end
end

function show_funcs (file)
   for i, body in func_bodies(file) do
      io.write(string.format("Group[%d]:\n", i))
      for k, v in ipairs(body) do
         print(v)
      end
      print()
   end
end

Here is a sample interaction:

> show_funcs("test_funcs3.lua")
Group[1]:
   local a = 2*x
   local b = 2*y
   return a + b

Group[2]:
   local c = x / 2
   local d = y / 2
   return c - d

Group[3]:
   local a = x + 1
   local b = y + 2
   return a * b

Group[4]:
   local a = x - 1
   local b = y - 2
   return a / 2 - b / 2

Here is the file used for the above test:

function f_1 (x, y)
   local a = 2*x
   local b = 2*y
   return a + b
end
local function f_2 (x, y)
   local c = x / 2
   local d = y / 2
   return c - d
end
g_1 = function (x, y)
   local a = x + 1
   local b = y + 2
   return a * b
end
local g_2 = function (x, y)
   local a = x - 1
   local b = y - 2
   return a / 2 - b / 2
end

Here is your example code with some single-line functions added:

function foo(a, b, c)
    local d = a + b
    local e = b - c
    return d *e
end
function boo(person)
    if (person.age == 18) then
        print("Adult")
    else
        print("Kid")
    end

    if (person.money > 20000) then
        print("Rich")
    else
        print("poor")
    end
end

function f1 (x, y) return x + y; end
local function f2 (x, y) return x - y; end
g1 = function (x, y) return x * y; end
local g2 = function (x, y) return x / y; end

Sample interaction:

> show_funcs("test_funcs2.lua")
Group[1]:
    local d = a + b
    local e = b - c
    return d *e

Group[2]:
    if (person.age == 18) then
        print("Adult")
    else
        print("Kid")
    end

    if (person.money > 20000) then
        print("Rich")
    else
        print("poor")
    end

Group[3]:
return x + y;

Group[4]:
return x - y;

Group[5]:
return x * y;

Group[6]:
return x / y;
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • @x84x4-- I added an improvement to my solution to allow locally scoped functions to be detected. – ad absurdum Feb 13 '17 at 22:27
  • Ok thanks but what if the function is declared on a single line ? Moreover, your test functions are quite simple too. Does this work with several if then statements ? – x84x4 Feb 14 '17 at 00:43
  • It will work with arbitrarily complicated functions, so long as the definitions start and end in the first column, and intervening lines are indented. It works for your example code. Single line functions could probably be added as a special case. Nested function definitions would not be so simple. – ad absurdum Feb 14 '17 at 00:51
  • @x84x4-- I have added a capability for detecting functions defined on a single line. – ad absurdum Feb 14 '17 at 01:36