Restructure and removal of some debug options.
This commit is contained in:
parent
69e5755c5f
commit
33daa3b8be
168
function.lua
168
function.lua
|
@ -99,14 +99,14 @@ local function get_function_info (f)
|
|||
|
||||
if info.source:sub(1,1) == '=' then info.defined_how = 'C'
|
||||
elseif info.source:sub(1,1) == '@' then info.defined_how = 'file'
|
||||
elseif info.source:find'^%w+.lua$' then info.defined_how = 'file' -- Hotfix for Love2d boot.lua issue.
|
||||
elseif info.source:find'^%w+.lua$' then info.defined_how = 'file' -- XXX: Hotfix for Love2d boot.lua issue.
|
||||
else info.defined_how = 'string'
|
||||
end
|
||||
|
||||
if info.builtin and LIBRARY[f] then
|
||||
info.name = LIBRARY[f].name
|
||||
info.params[1] = LIBRARY[f].para
|
||||
info.doc = LIBRARY[f].docs
|
||||
info.docs = LIBRARY[f].docs
|
||||
end
|
||||
|
||||
return info
|
||||
|
@ -125,9 +125,41 @@ local function get_line_index (str, line_nr)
|
|||
return index
|
||||
end
|
||||
|
||||
local function get_function_paramlist_and_body (info)
|
||||
-- Will attempt to find a string which refer to the function. This will
|
||||
-- possibly require opening a file.
|
||||
local function get_docs_from_function_body (body, max_index)
|
||||
-- Finds the documentation lines of a function.
|
||||
-- Also returns the remaining non-documentation lines.
|
||||
|
||||
assert(type(body) == 'string')
|
||||
|
||||
local doc_lines = {}
|
||||
for line in body:sub(1, max_index):gmatch('[^\n]+', true) do
|
||||
if not line:match '^%s*$' then
|
||||
local line_text = line:match '^%s*%-%-%s*(.*)%s*$'
|
||||
doc_lines[#doc_lines+1] = line_text
|
||||
if not line_text then break end
|
||||
end
|
||||
end
|
||||
return table.concat(doc_lines, '\n'):match '^%s*(.-)%s*$'
|
||||
end
|
||||
|
||||
local function get_docs_split_index (body)
|
||||
local index = 1
|
||||
while index <= #body do
|
||||
local next_newline = body:find('\n', index + 1) or -1
|
||||
if body:sub(index, next_newline):match('^%s*$') or body:sub(index, next_newline):match('^%s*%-%-%s*(.*)%s*$') then
|
||||
index = next_newline
|
||||
else
|
||||
return index
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
local function get_function_body_info (info)
|
||||
-- Will attempt to expand `info` with extra info found by looking at the
|
||||
-- source code. This could require opening a file.
|
||||
-- There is no guarentee that the function body it finds is correct, or even
|
||||
-- that it finds any.
|
||||
|
||||
-- Error check
|
||||
assert(type(info) == 'table')
|
||||
|
@ -149,19 +181,29 @@ local function get_function_paramlist_and_body (info)
|
|||
local start_line_index = get_line_index(str, info.linedefined)
|
||||
local end_line_index = get_line_index(str, info.lastlinedefined + 1)
|
||||
|
||||
-- Now find the function parameters and the function body.
|
||||
-- Now find some info about the function.
|
||||
-- NOTE: function_params is currently not used for anything.
|
||||
local function_name, function_params, function_body = str:sub(start_line_index, end_line_index):match(FUNCTION_DEFINITION_MATCH)
|
||||
-- TODO: Use function_name for something.
|
||||
if type(function_params) ~= 'string' or type(function_body) ~= 'string' then
|
||||
|
||||
if type(function_body) ~= 'string' then
|
||||
error(('[pretty.function/internal]: Could not find the function defined on lines %i-%i (indices %i-%i) for string:\n\n%s\n'):format(info.linedefined, info.lastlinedefined, start_line_index, end_line_index, str))
|
||||
end
|
||||
-- And return them.
|
||||
return function_params, function_body:match('^%s*(.-)%s*$'), function_name:match('^%s*(.-)%s*$')
|
||||
|
||||
local function_body = function_body:match '^%s*(.-)%s*$'
|
||||
local pivot_index = get_docs_split_index (function_body)
|
||||
|
||||
info.name = info.name or function_name:match '^%s*(.-)%s*$'
|
||||
info.docs = info.docs or get_docs_from_function_body(function_body, pivot_index)
|
||||
info.body = info.body or function_body:sub(pivot_index, -1):match '^%s*(.-)%s*$'
|
||||
|
||||
if info.name == '' then info.name = nil end
|
||||
if info.docs == '' then info.docs = nil end
|
||||
|
||||
return info
|
||||
end
|
||||
|
||||
local function get_function_string (...)
|
||||
return string.format('function %s %s end', get_function_paramlist_and_body(...))
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
-- Text handling
|
||||
|
||||
local function width_of_strings_in_l (l, start_i, end_i)
|
||||
-- FIXME: Copy of the one in pretty.lua
|
||||
|
@ -179,29 +221,6 @@ local function add_indent_to_string (str, indent)
|
|||
return indent .. str:gsub('\n', '\n'..indent)
|
||||
end
|
||||
|
||||
local function get_docs_from_function_body (func_body)
|
||||
-- Finds the documentation lines of a function.
|
||||
-- Also returns the remaining non-documentation lines.
|
||||
|
||||
assert(type(func_body) == 'string')
|
||||
|
||||
local doc_lines, not_doc_lines, still_docs = {}, {}, true
|
||||
for line in func_body:gmatch('[^\n]+', true) do
|
||||
if still_docs and not line:match('^%s*$') then
|
||||
local line_text = line:match('^%s*%-%-%s*(.*)%s*$')
|
||||
if line_text then
|
||||
doc_lines[#doc_lines+1] = line_text
|
||||
else
|
||||
still_docs = false
|
||||
end
|
||||
end
|
||||
if not still_docs then
|
||||
not_doc_lines[#not_doc_lines+1] = line
|
||||
end
|
||||
end
|
||||
return table.concat(doc_lines, '\n'), table.concat(not_doc_lines, '\n')
|
||||
end
|
||||
|
||||
local function wrap_text (text, max_width)
|
||||
local l, i, last_i = {}, max_width, 1
|
||||
repeat
|
||||
|
@ -220,52 +239,20 @@ end
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local function format_function_with_closure (value, depth, l, format_value)
|
||||
assert(type(value) == 'function')
|
||||
assert(type(depth) == 'number' and type(l) == 'table' and type(format_value) == 'function')
|
||||
|
||||
local info = get_function_info(value)
|
||||
|
||||
local function_str = get_function_string(info)
|
||||
|
||||
if info.nups > 0 then l[#l+1] = '(function () ' end
|
||||
-- Upvalues
|
||||
for k, v in pairs(info.ups) do
|
||||
l[#l+1] = 'local '
|
||||
l[#l+1] = tostring(k)
|
||||
l[#l+1] = ' = '
|
||||
format_value(v, depth + 1, l)
|
||||
l[#l+1] = '; '
|
||||
end
|
||||
-- Return function
|
||||
if info.nups > 0 then l[#l+1] = 'return ' end
|
||||
l[#l+1] = function_str
|
||||
--
|
||||
if info.nups > 0 then l[#l+1] = ' end)()' end
|
||||
end
|
||||
|
||||
return function (value, depth, l, format_value)
|
||||
assert(type(value) == 'function')
|
||||
assert(type(depth) == 'number' and type(l) == 'table' and type(format_value) == 'function')
|
||||
|
||||
local info = get_function_info(value)
|
||||
|
||||
if l.options._include_closure and not info.builtin then
|
||||
return format_function_with_closure(value, depth, l, format_value)
|
||||
end
|
||||
|
||||
local function_params, function_body = nil, '...'
|
||||
|
||||
if info.defined_how == 'string' or not info.doc then
|
||||
local _, body, name = get_function_paramlist_and_body(info)
|
||||
local docs, body = get_docs_from_function_body(body)
|
||||
body = body:match('^%s*(.-)%s*$')
|
||||
if #body <= NR_CHARS_IN_LONG_FUNCTION_BODY and not body:find '\n' and not body:find(FUNCTION_KEYWORD_MATCH) then
|
||||
if info.defined_how == 'string' then function_body = body end
|
||||
end
|
||||
if not info.docs then
|
||||
info = get_function_body_info(info)
|
||||
|
||||
info.doc = not info.doc and docs and docs ~= '' and docs
|
||||
info.name = not info.name and name and name ~= '' and name
|
||||
if #info.body <= NR_CHARS_IN_LONG_FUNCTION_BODY and not info.body:find '\n' and not info.body:find(FUNCTION_KEYWORD_MATCH) then
|
||||
if info.defined_how == 'string' then function_body = info.body end
|
||||
end
|
||||
end
|
||||
|
||||
if info.builtin and l.options.short_builtins then
|
||||
|
@ -311,15 +298,15 @@ return function (value, depth, l, format_value)
|
|||
end
|
||||
|
||||
-- Doc
|
||||
if info.doc then
|
||||
if info.docs then
|
||||
l[#l+1] = '\n'
|
||||
local indent = l.options.indent .. '-- '
|
||||
local docs = not info.builtin and info.doc or wrap_text(info.doc, 80 - #indent)
|
||||
local docs = not info.builtin and info.docs or wrap_text(info.docs, 80 - #indent)
|
||||
l[#l+1] = add_indent_to_string(docs, indent)
|
||||
end
|
||||
|
||||
-- source
|
||||
if info.doc or info.name then -- Do nothing
|
||||
if info.docs or info.name then -- Do nothing
|
||||
elseif info.defined_how == 'string' then
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '-- Loaded from string'
|
||||
|
@ -334,39 +321,12 @@ return function (value, depth, l, format_value)
|
|||
end
|
||||
|
||||
-- upvalues
|
||||
if info.nups > 0 and (not info.builtin and not info.doc) then
|
||||
if info.nups > 0 and (not info.builtin and not info.docs) then
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '-- Up values: '
|
||||
format_value(info.ups, depth + 1, l)
|
||||
end
|
||||
|
||||
if l.options._all_function_info then
|
||||
-- NOTE: This is for testing/debugging/experimentation purposes, and is
|
||||
-- not designed to be pretty.
|
||||
|
||||
-- Native
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '-- Native Representation: '
|
||||
l[#l+1] = tostring(value)
|
||||
|
||||
-- Function body
|
||||
if info.defined_how ~= 'C' then
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--[[ Function Body:\n\t'
|
||||
l[#l+1] = add_indent_to_string(get_function_string(info), l.options.indent)
|
||||
l[#l+1] = indent
|
||||
l[#l+1] = '--]]'
|
||||
end
|
||||
|
||||
-- Full info
|
||||
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] = '--]]'
|
||||
end
|
||||
|
||||
-- Ignore spacing and function body if it's a Λ string.
|
||||
if function_body ~= '' then
|
||||
l[#l+1] = '\n'
|
||||
|
|
|
@ -470,8 +470,6 @@ local DEBUG_OPTION_USED = { }
|
|||
|
||||
local KNOWN_OPTIONS = {
|
||||
_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 },
|
||||
indent = { type = 'string', default = ' ' },
|
||||
|
|
|
@ -442,128 +442,5 @@ if HAS_UNICODE_IDEN then
|
|||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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 = { _all_function_info = true, max_depth = 2 },
|
||||
input = function() end,
|
||||
expect = '',
|
||||
}
|
||||
--]]
|
||||
|
||||
return SUITE
|
||||
|
|
Loading…
Reference in New Issue
Block a user