Updated test_function.lua
to reflect the new design. The system also warns against using experimental and debug options.
This commit is contained in:
parent
4a41111d1e
commit
3bb599e976
28
function.lua
28
function.lua
|
@ -123,8 +123,8 @@ local function get_function_paramlist_and_body (str, start_line, end_line)
|
|||
local start_line_index = get_line_index(str, start_line)
|
||||
local end_line_index = get_line_index(str, end_line + 1)
|
||||
|
||||
local function_params, function_body = str:sub(start_line_index, end_line_index):match('function%s*[a-zA-Z0-9_.]*%s*(%([a-zA-Z0-9_,. \t]*%))[ \t]*(.-)[ \t]*end')
|
||||
--print(function_params, function_body)
|
||||
local function_params, function_body = str:sub(start_line_index, end_line_index):match('.*function%s*[a-zA-Z0-9_.]*%s*(%([a-zA-Z0-9_,. \t]*%))[ \t]*(.+)[ \t]*end')
|
||||
function_body = function_body:match('^%s*(.-)%s*$')
|
||||
assert(type(function_params) == 'string' and type(function_body) == 'string')
|
||||
return function_params, function_body
|
||||
end
|
||||
|
@ -160,11 +160,13 @@ local function add_indent_to_string (str, indent)
|
|||
assert(type(indent) == 'string')
|
||||
|
||||
local l = {}
|
||||
for line in string.gmatch(str, '\n', true) do
|
||||
for line in str:gmatch('[^\n]+', true) do
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = line
|
||||
l[#l+1] = '\n'
|
||||
end
|
||||
return table.concat(l, '\n')
|
||||
if l[#l] == '\n' then l[#l] = nil end
|
||||
return table.concat(l, '')
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -204,7 +206,7 @@ return function (value, depth, l, format_value)
|
|||
|
||||
local info = get_function_info(value)
|
||||
|
||||
if l.options.include_closure and not info.builtin then
|
||||
if l.options._include_closure and not info.builtin then
|
||||
return format_function_with_closure(value, depth, l, format_value)
|
||||
end
|
||||
|
||||
|
@ -237,7 +239,7 @@ return function (value, depth, l, format_value)
|
|||
l[#l+1] = { 'align', 'func_def', width_of_strings_in_l(l, top_before) }
|
||||
|
||||
-- Cleanup and finish
|
||||
if not l.options.more_function_info or depth ~= 0 then
|
||||
if depth ~= 0 then
|
||||
l[#l+1] = (function_body:sub(1,1) == '\n') and '' or ' '
|
||||
l[#l+1] = function_body
|
||||
l[#l+1] = { 'align', 'func_end', #function_body }
|
||||
|
@ -288,16 +290,22 @@ return function (value, depth, l, format_value)
|
|||
-- NOTE: This is for testing/debugging/experimentation purposes, and is
|
||||
-- not designed to be pretty.
|
||||
|
||||
-- Native
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--[[ Function Body:\n\t'
|
||||
l[#l+1] = '-- native_repr: '
|
||||
l[#l+1] = tostring(value)
|
||||
|
||||
-- Function body
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--[[ function_body:\n\t'
|
||||
l[#l+1] = add_indent_to_string(get_function_str_from_file(info.short_src, info.linedefined, info.lastlinedefined), l.options.indent)
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--]]'
|
||||
|
||||
-- Full info
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--[[\n\tNative repr:'
|
||||
l[#l+1] = tostring(value)
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--[[ full_info:\n'
|
||||
info.env = nil
|
||||
format_value(info, depth + 1, l)
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--]]'
|
||||
|
|
17
pretty.lua
17
pretty.lua
|
@ -463,19 +463,19 @@ setmetatable(StringBuilder, {
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local DEBUG_OPTIONS = { _all_function_info = true, _table_addr_comment = true }
|
||||
local DEBUG_OPTION_USED = { }
|
||||
|
||||
local KNOWN_OPTIONS = {
|
||||
_table_addr_comment = { type = 'boolean', default = false },
|
||||
_all_function_info = { type = 'boolean', default = false },
|
||||
_table_addr_comment = { type = 'boolean', default = false, debug = 'debug' },
|
||||
_all_function_info = { type = 'boolean', default = false, debug = 'debug' },
|
||||
_include_closure = { type = 'boolean', default = false, debug = 'experimental' },
|
||||
|
||||
cut_strings = { type = 'boolean', default = false },
|
||||
include_closure = { type = 'boolean', default = false },
|
||||
indent = { type = 'string', default = ' ' },
|
||||
max_depth = { type = 'number', default = math.huge },
|
||||
embed_loaded_funcs = { type = 'boolean', default = false },
|
||||
more_function_info = { type = 'boolean', default = false },
|
||||
embed_loaded_funcs = { type = 'boolean', default = false }, -- TODO: Outphase this, in favor of automatically embedding "small enough" functions.
|
||||
recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} },
|
||||
short_builtins = { type = 'boolean', default = false },
|
||||
short_builtins = { type = 'boolean', default = false }, -- TODO: Outphase this. Rather automatically use the short versions in places where it would be strange to find the function, like keys, etc.
|
||||
}
|
||||
|
||||
local function ensure_that_all_options_are_known (options)
|
||||
|
@ -488,6 +488,9 @@ local function ensure_that_all_options_are_known (options)
|
|||
error(('[pretty]: Bad value given to option %s: %s (%s). Expected value of type %s'):format(option_name, option_value, type(option_value), KNOWN_OPTIONS[option_name].type), 2)
|
||||
elseif KNOWN_OPTIONS[option_name].accepted and not KNOWN_OPTIONS[option_name].accepted[option_value] then
|
||||
error(('[pretty]: Bad value given to option %s: %s (%s). Expected one of: %s'):format(option_name, option_value, type(option_value), table.concat(KNOWN_OPTIONS[option_name].accepted, ', ')), 2)
|
||||
elseif KNOWN_OPTIONS[option_name].debug and not DEBUG_OPTION_USED[option_name] then
|
||||
DEBUG_OPTION_USED[option_name] = true
|
||||
print(('[pretty]: Using %s option "%s".\n Please note that this option may change at any time. It is not stable,\n not tested, and may indeed break or be removed without warning.'):format(KNOWN_OPTIONS[option_name].debug, option_name))
|
||||
end
|
||||
end
|
||||
-- Assign default values
|
||||
|
|
|
@ -10,217 +10,153 @@ local HAS_ADV_GETLOCAL = not not debug.getinfo(1, 'u').nparams -- Lua 5.1 compa
|
|||
if not loadstring then loadstring = load end -- Lua 5.3 compat
|
||||
|
||||
local function format_test (t)
|
||||
if t.longterm then return end
|
||||
if t.adv_getlocal and not HAS_ADV_GETLOCAL then return end
|
||||
if t.single then
|
||||
return SUITE:addTest(t.name or t.expect, function ()
|
||||
local format = format({t.input}, t.options):gsub('^{%s+(.-)%s+}$', function(a) return '{ '..a..' }' end)
|
||||
assert_equal('{ '..t.expect..' }', format)
|
||||
end, { line = debug.getinfo(2).currentline })
|
||||
end
|
||||
SUITE:addTest(t.name or t.expect, function ()
|
||||
local input_value = t.input
|
||||
local input_options = t.options
|
||||
local expected_result = t.expect
|
||||
local actual_result = format(input_value, input_options)
|
||||
if not t.approx or type(actual_result) ~= 'string' then
|
||||
assert_equal(expected_result, actual_result)
|
||||
else
|
||||
if not actual_result:match(expected_result) then
|
||||
error(ASSERT_ERROR_APPROX:format(expected_result, actual_result))
|
||||
end
|
||||
end
|
||||
assert_equal(t.expect, format(t.input, t.options))
|
||||
end, { line = debug.getinfo(2).currentline })
|
||||
end
|
||||
|
||||
local function curline (delta)
|
||||
return debug.getinfo(2).currentline + (delta or 0)
|
||||
end
|
||||
local function curline (delta) return debug.getinfo(2).currentline + (delta or 0) end
|
||||
|
||||
local TEST_UPVALUE = 42
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function printing
|
||||
-- Basic inline functions
|
||||
|
||||
format_test {
|
||||
name = 'Basic function formatting',
|
||||
adv_getlocal = true,
|
||||
single = true,
|
||||
input = function () end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Function with a single argument',
|
||||
adv_getlocal = true,
|
||||
single = true,
|
||||
input = function (a) end,
|
||||
expect = 'function (a) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Function with multiple arguments',
|
||||
adv_getlocal = true,
|
||||
single = true,
|
||||
input = function (a, b) end,
|
||||
expect = 'function (a, b) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Function with vararg',
|
||||
adv_getlocal = true,
|
||||
single = true,
|
||||
input = function (...) end,
|
||||
expect = 'function (...) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Function with vararg and multiple arguments',
|
||||
adv_getlocal = true,
|
||||
single = true,
|
||||
input = function (a, b, ...) end,
|
||||
expect = 'function (a, b, ...) ... end',
|
||||
}
|
||||
|
||||
do
|
||||
local SOME_RANDOM_UPVALUE = false
|
||||
|
||||
format_test {
|
||||
adv_getlocal = true,
|
||||
input = function () l = SOME_RANDOM_UPVALUE end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
adv_getlocal = true,
|
||||
input = function () SOME_RANDOM_UPVALUE = true end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
-- More function info is ignored if not at depth 0.
|
||||
adv_getlocal = true,
|
||||
input = { a = function () SOME_RANDOM_UPVALUE = true end },
|
||||
options = { more_function_info = true },
|
||||
expect = '{ a = function () ... end }',
|
||||
}
|
||||
|
||||
|
||||
local func_line = curline(2) -- Must be exactly 2 lines above function
|
||||
format_test {
|
||||
input = function () l = SOME_RANDOM_UPVALUE end,
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { SOME_RANDOM_UPVALUE = false }\n\n ...\nend'
|
||||
}
|
||||
|
||||
local func_line = curline(2) -- Must be exactly 2 lines above function
|
||||
format_test {
|
||||
input = function () SOME_RANDOM_UPVALUE = true end,
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { SOME_RANDOM_UPVALUE = false }\n\n ...\nend'
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local func_line = curline(2) -- Must be exactly 2 lines above function
|
||||
format_test {
|
||||
input = function () end,
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n ...\nend'
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local index = 0
|
||||
|
||||
format_test {
|
||||
input = function () index = index + 1; return index end,
|
||||
adv_getlocal = true,
|
||||
expect = 'function () ... end'
|
||||
}
|
||||
|
||||
local func_line = curline(2) -- Must be exactly 2 lines above function
|
||||
format_test {
|
||||
input = function () index = index + 1; return index end,
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { index = 0 }\n\n ...\nend'
|
||||
}
|
||||
end
|
||||
|
||||
format_test {
|
||||
name = 'Closures don\'t affect functions printed in single mode 1',
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () end')(),
|
||||
expect = 'function () end',
|
||||
single = true,
|
||||
input = function () l = TEST_UPVALUE end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Closures don\'t affect functions printed in single mode 2',
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () return function () end end')(),
|
||||
expect = 'function () return function () end end',
|
||||
single = true,
|
||||
input = function () TEST_UPVALUE = true end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Basic elaborate functions
|
||||
|
||||
local func_line = curline(3)
|
||||
format_test {
|
||||
name = 'Singleline elaborate function',
|
||||
input = function () end,
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n ...\nend',
|
||||
}
|
||||
|
||||
local func_line = curline(3)
|
||||
format_test {
|
||||
name = 'Multiline elaborate function',
|
||||
input = function ()
|
||||
end,
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+1)..']\n\n ...\nend',
|
||||
}
|
||||
|
||||
local func_line = curline(3) -- Must be exactly 3 lines above function
|
||||
format_test {
|
||||
name = 'Elaborate function with upvalue included 1',
|
||||
input = function () l = TEST_UPVALUE end,
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () return function () end\nend')()(),
|
||||
expect = 'function () end',
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { TEST_UPVALUE = 42 }\n\n ...\nend'
|
||||
}
|
||||
|
||||
local func_line = curline(3) -- Must be exactly 3 lines above function
|
||||
format_test {
|
||||
-- NOTE: This is HARD to fix. It's thus longerterm
|
||||
name = 'Elaborate function with upvalue included 2',
|
||||
input = function () TEST_UPVALUE = true end,
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () return function () end end')()(),
|
||||
expect = 'function () end',
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { TEST_UPVALUE = 42 }\n\n ...\nend'
|
||||
}
|
||||
|
||||
local func_line = curline(3)
|
||||
format_test {
|
||||
-- More function info allows one to even get the function whole, if it was defined in a string.
|
||||
input = loadstring('return function (a, b) return a + b end')(),
|
||||
options = { embed_loaded_funcs = true },
|
||||
expect = 'function (a, b) return a + b end',
|
||||
name = 'Elaborate function with documentation',
|
||||
input = function ()
|
||||
-- Hello World
|
||||
if true then return false end
|
||||
return true
|
||||
end,
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+1)..']\n -- Hello World\n\n ...\nend',
|
||||
}
|
||||
|
||||
format_test {
|
||||
-- More function info allows one to even get the function whole, if it was defined in a string.
|
||||
input = loadstring('return function (a, b)\n return a + b\nend')(),
|
||||
options = { embed_loaded_funcs = true },
|
||||
expect = 'function (a, b)\n return a + b\nend',
|
||||
}
|
||||
|
||||
do
|
||||
local func_line = curline(2) -- Must be exactly 2 lines above function
|
||||
format_test {
|
||||
input = function ()
|
||||
-- NOTE: This function must cover 3 lines of code!
|
||||
end,
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+2)..']\n\n ...\nend',
|
||||
}
|
||||
|
||||
local func_line = curline(2) -- Must be exactly 2 lines above function
|
||||
format_test {
|
||||
input = function () --[[ NOTE: This function must cover a single line of code! ]] end,
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n ...\nend',
|
||||
}
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- Builtins
|
||||
|
||||
format_test {
|
||||
single = true,
|
||||
input = math.abs,
|
||||
expect = 'builtin function (x) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = math.abs,
|
||||
options = { more_function_info = true },
|
||||
expect = 'builtin function (x)\n -- math.abs\n -- Returns the absolute value of x.\n\n ...\nend',
|
||||
}
|
||||
|
||||
format_test {
|
||||
single = true,
|
||||
input = math.random,
|
||||
expect = 'builtin function ([m [, n]) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = math.random,
|
||||
options = { more_function_info = true },
|
||||
expect = 'builtin function ([m [, n])\n -- math.random\n -- When called without arguments, returns a uniform pseudo-random real number in the range [0,1). When called with an integer number m, math.random returns a uniform pseudo-random integer in the range [1, m]. When called with two integer numbers m and n, math.random returns a uniform pseudo-random integer in the range [m, n].\n\n ...\nend',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = string.byte,
|
||||
options = { more_function_info = true },
|
||||
expect = 'builtin function (s [, i [, j]])\n -- string.byte\n -- Returns the internal numerical codes of the characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the default value for j is i.\n -- Note that numerical codes are not necessarily portable across platforms.\n\n ...\nend',
|
||||
}
|
||||
|
||||
|
@ -228,126 +164,69 @@ format_test {
|
|||
-- in a standard enviroment, return that name, instead of other complex info.
|
||||
|
||||
format_test {
|
||||
-- NOTE: These tests may be counter to intention, soon.
|
||||
input = math.random,
|
||||
options = { short_builtins = true },
|
||||
expect = 'math.random',
|
||||
}
|
||||
|
||||
format_test {
|
||||
-- NOTE: These tests may be counter to intention, soon.
|
||||
input = { math.cos, math.sin, math.abs },
|
||||
options = { short_builtins = true },
|
||||
expect = '{ math.cos, math.sin, math.abs }',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Replace function with short version, if key',
|
||||
input = { [math.random] = 365 },
|
||||
expect = '{ [math.random] = 365 }',
|
||||
}
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Closure creation
|
||||
-- include_closure option: If a function uses upvalues, return code that creates
|
||||
-- a closure for that function, including the function code.
|
||||
|
||||
-- NOTE: Without AST traversal, it's impossible to garentee that values refered
|
||||
-- by multiple recursive closures are the same, because they can actually refer
|
||||
-- to different variables with the same name, as the following example shows:
|
||||
--
|
||||
-- local val_a = 1
|
||||
-- local func_a = function () return val_a end
|
||||
-- local val_a = 2
|
||||
-- local func_c = function () return func_a() + val_a end
|
||||
|
||||
do
|
||||
format_test {
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = function (a, b) return (a > b) and a or b end,
|
||||
expect = 'function (a, b) return (a > b) and a or b end',
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local input_func = (function ()
|
||||
local i = 0
|
||||
return function (a) i = i + a; return i end
|
||||
end)()
|
||||
for i = 1, 10 do input_func(1) end
|
||||
|
||||
format_test {
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = input_func,
|
||||
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local input_func = loadstring([[
|
||||
return function ()
|
||||
local i = 0
|
||||
return function (a) i = i + a; return i end
|
||||
end]])()()
|
||||
for i = 1, 10 do input_func(1) end
|
||||
|
||||
format_test {
|
||||
name = 'Can include variables in closure',
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = input_func,
|
||||
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
|
||||
}
|
||||
end
|
||||
-- Embedding functions loaded with loadstring
|
||||
|
||||
format_test {
|
||||
name = 'Can include functions in closure',
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = (function ()
|
||||
local custom_max = function (a, b) return (a > b) and a or b end
|
||||
return function (a, b) return custom_max(a, b) + 2 end
|
||||
end)(),
|
||||
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
|
||||
single = true,
|
||||
name = 'It\'s possible to get loadstring functions whole',
|
||||
input = loadstring('return function (a, b) return a + b end')(),
|
||||
options = { embed_loaded_funcs = true },
|
||||
expect = 'function (a, b) return a + b end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Can include functions defined with `function <name> () ...` style',
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = (function ()
|
||||
local function custom_max (a, b) return (a > b) and a or b end
|
||||
return function (a, b) return custom_max(a, b) + 2 end
|
||||
end)(),
|
||||
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
|
||||
single = true,
|
||||
name = 'Whitespace is automatically stripped from loadstring functions',
|
||||
input = loadstring('return function (a, b)\n return a + b\nend')(),
|
||||
options = { embed_loaded_funcs = true },
|
||||
expect = 'function (a, b) return a + b end',
|
||||
}
|
||||
|
||||
do
|
||||
local custom_max = function (a, b) return (a > b) and a or b end
|
||||
format_test {
|
||||
name = 'Can include functions from outside scope into closure',
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = (function ()
|
||||
return function (a, b) return custom_max(a, b) + 2 end
|
||||
end)(),
|
||||
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local a_func = function (x) return x + 2 end
|
||||
local b_func = function (x) return a_func(x) * a_func(x) end
|
||||
local c_func = function (x) return b_func(a_func(x)) end
|
||||
format_test {
|
||||
name = 'Can support recursive closures',
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
input = c_func,
|
||||
expect = '(function () local b_func = (function () local a_func = function (x) return x + 2 end; return function (x) return a_func(x) * a_func(x) end end)(); local a_func = function (x) return x + 2 end; return function (x) return b_func(a_func(x)) end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
format_test {
|
||||
single = true,
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () return function () end\nend')()(),
|
||||
expect = 'function () end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Closures do not affect builtins',
|
||||
input = math.abs,
|
||||
options = { more_function_info = true, include_closure = true },
|
||||
expect = 'builtin function (x)\n -- math.abs\n -- Returns the absolute value of x.\n\n ...\nend',
|
||||
name = 'When finding the correct function becomes too hard, just ignore it 1',
|
||||
single = true,
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () return function () end end')(),
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'When finding the correct function becomes too hard, just ignore it 2',
|
||||
single = true,
|
||||
adv_getlocal = true,
|
||||
options = { embed_loaded_funcs = true },
|
||||
input = loadstring('return function () return function () end end')()(),
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -400,13 +279,124 @@ format_test {
|
|||
expect = '{\n a = function (ψ) ... end\n b = function (a) ... end\n}',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Closure creation
|
||||
-- _include_closure option: If a function uses upvalues, return code that creates
|
||||
-- a closure for that function, including the function code.
|
||||
|
||||
-- NOTE: Without AST traversal, it's impossible to garentee that values refered
|
||||
-- by multiple recursive closures are the same, because they can actually refer
|
||||
-- to different variables with the same name, as the following example shows:
|
||||
--
|
||||
-- local val_a = 1
|
||||
-- local func_a = function () return val_a end
|
||||
-- local val_a = 2
|
||||
-- local func_c = function () return func_a() + val_a end
|
||||
|
||||
-- NOTE: The following tests are all EXPERIMENTAL! All of the tests use features
|
||||
-- which may change at any time.
|
||||
|
||||
format_test {
|
||||
name = 'Closures do not affect non-upvalue function',
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = function (a, b) return (a > b) and a or b end,
|
||||
expect = 'function (a, b) return (a > b) and a or b end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Closures do not affect builtins',
|
||||
input = math.abs,
|
||||
options = { _include_closure = true },
|
||||
expect = 'builtin function (x)\n -- math.abs\n -- Returns the absolute value of x.\n\n ...\nend',
|
||||
}
|
||||
|
||||
do
|
||||
local input_func = (function ()
|
||||
local i = 0
|
||||
return function (a) i = i + a; return i end
|
||||
end)()
|
||||
for i = 1, 10 do input_func(1) end
|
||||
|
||||
format_test {
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = input_func,
|
||||
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local input_func = loadstring([[
|
||||
return function ()
|
||||
local i = 0
|
||||
return function (a) i = i + a; return i end
|
||||
end]])()()
|
||||
for i = 1, 10 do input_func(1) end
|
||||
|
||||
format_test {
|
||||
name = 'Can include variables in closure',
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = input_func,
|
||||
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
format_test {
|
||||
name = 'Can include functions in closure',
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = (function ()
|
||||
local custom_max = function (a, b) return (a > b) and a or b end
|
||||
return function (a, b) return custom_max(a, b) + 2 end
|
||||
end)(),
|
||||
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
|
||||
}
|
||||
|
||||
format_test {
|
||||
name = 'Can include functions defined with `function <name> () ...` style',
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = (function ()
|
||||
local function custom_max (a, b) return (a > b) and a or b end
|
||||
return function (a, b) return custom_max(a, b) + 2 end
|
||||
end)(),
|
||||
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
|
||||
}
|
||||
|
||||
do
|
||||
local custom_max = function (a, b) return (a > b) and a or b end
|
||||
format_test {
|
||||
name = 'Can include functions from outside scope into closure',
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = (function ()
|
||||
return function (a, b) return custom_max(a, b) + 2 end
|
||||
end)(),
|
||||
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local a_func = function (x) return x + 2 end
|
||||
local b_func = function (x) return a_func(x) * a_func(x) end
|
||||
local c_func = function (x) return b_func(a_func(x)) end
|
||||
format_test {
|
||||
name = 'Can support recursive closures',
|
||||
adv_getlocal = true,
|
||||
options = { _include_closure = true },
|
||||
input = c_func,
|
||||
expect = '(function () local b_func = (function () local a_func = function (x) return x + 2 end; return function (x) return a_func(x) * a_func(x) end end)(); local a_func = function (x) return x + 2 end; return function (x) return b_func(a_func(x)) end end)()',
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--[[
|
||||
format_test {
|
||||
adv_getlocal = true,
|
||||
options = { more_function_info = true, _all_function_info = true, max_depth = 2 },
|
||||
options = { _all_function_info = true, max_depth = 2 },
|
||||
input = function() end,
|
||||
expect = '',
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user