local args = {...} local progName = args[1] local progArgs = {unpack(args, 2)} local func, err = loadfile(progName) if not func then error(err) end function printf(...) print(string.format(...)) end local handle = fs.open(progName, "r") local content = " " .. handle.readAll() .. " " local contentCopy = content handle.close() --[-[ Detecting locals local occurenceBegin = 0 local lineNumber = 1 local recordedLocals = {} repeat occurenceBegin = string.find(content, "[%-%[\"'\nl]", occurenceBegin + 1) if occurenceBegin then if string.sub(content, occurenceBegin - 1, occurenceBegin - 1) ~= "\\" then local character = string.sub(content, occurenceBegin, occurenceBegin) if character == "[" then local blockBegin, blockEnd, level = string.find(content, "%[(=*)%[", occurenceBegin) if blockBegin == occurenceBegin then occurenceBegin = string.find(content, "[^\\]%]" .. level .. "%]", occurenceBegin) + 1 end elseif character == "\"" then occurenceBegin = string.find(content, "[^\\]\"", occurenceBegin) + 1 elseif character == "'" then occurenceBegin = string.find(content, "[^\\]'", occurenceBegin) + 1 elseif character == "\n" then lineNumber = lineNumber + 1 elseif character == "-" then local blockBegin, blockEnd, level = string.find(content, "%-%-%[(=*)%[", occurenceBegin) if blockBegin == occurenceBegin then local fullBlockBegin = occurenceBegin + 3 occurenceBegin = string.find(content, "%]" .. level .. "%]", occurenceBegin) for word in string.gmatch(string.sub(content, fullBlockBegin, occurenceBegin - 1), "\n") do lineNumber = lineNumber + 1 end elseif string.sub(content, occurenceBegin, occurenceBegin + 1) == "--" then local fullBlockBegin = occurenceBegin + 1 occurenceBegin = string.find(content, "[^\\]\n", occurenceBegin) for word in string.gmatch(string.sub(content, fullBlockBegin, occurenceBegin - 1), "\n") do lineNumber = lineNumber + 1 end end elseif character == "l" then if string.find(string.sub(content, occurenceBegin - 1, occurenceBegin + 5), "%slocal%s") then contentCopy = string.sub(contentCopy, 1, occurenceBegin - 1) .. string.rep(" ", 5) .. string.sub(contentCopy, occurenceBegin + 5, #contentCopy) local names = {} if not recordedLocals[lineNumber] then recordedLocals[lineNumber] = {} end local nameLast = occurenceBegin + 4 local nameBegin, nameEnd, name repeat nameBegin, nameEnd, name = string.find(content, "^%s*([_%a][_%w]*)%s*,", nameLast + 1) if nameBegin then table.insert(names, name) nameLast = nameEnd end until not nameBegin nameBegin, nameEnd, name = string.find(content, "^%s*([_%a][_%w]*)%s*", nameLast + 1) local isRealFunction = false if name == "function" then isRealFunction = true nameLast = nameEnd nameBegin, nameEnd, name = string.find(content, "^%s*([_%a][_%w]*)%s*", nameLast + 1) end nameBegin, nameEnd = string.find(content, "^%s*[_%a][_%w]*", nameLast + 1) table.insert(names, name) for ixName = 1, #names do recordedLocals[lineNumber][names[ixName]] = true end if not isRealFunction and not string.find(content, "%s*=", nameEnd) then contentCopy = string.sub(contentCopy, 1, occurenceBegin - 1) .. string.rep(" ", nameEnd - occurenceBegin + 1) .. string.sub(contentCopy, nameEnd + 1, #contentCopy) end occurenceBegin = nameEnd - 1 end end end end until not occurenceBegin --]] --[[ Local report local lreport = fs.open("lreport", "w") for key, value in pairs(recordedLocals) do lreport.writeLine(string.format("Line %i declares the following locals:", key)) for kkey, kvalue in pairs(value) do if kvalue then lreport.writeLine(string.format(" %s", kkey)) end end end lreport.close() --]] --[-[ contentCopy dump local ccreport = fs.open("ccreport", "w") ccreport.write(contentCopy) ccreport.close() --]] --[-[ Tracing local indexing = {} local callStack = {} local fenvs = {} local mglobals = {} -- gotta put _G into here with metatables - somehow fenvs.container = {} function fenvs.add(func, name, parent) local fenvp = {} fenvs.container[func] = fenvp fenvp.locals = {} fenvp.parent = parent fenvp.name = name function fenvp.getUpvalue(key) if fenvp.locals[key] then return fenvp.locals[key] end if fenvp.parent then return fenvp.parent.getUpvalue(key) end if mglobals[key] then return mglobals[key] end end function fenvp.setUpvalue(key, value) if fenvp.locals[key] then fenvp.locals[key] = value return end if fenvp.parent then fenvp.parent.setUpvalue(key, value) return end mglobals[key] = value end fenvp.env = setmetatable({}, { __newindex = function(tbl, key, value) local _, err = pcall(error, "", 3) local _, _, line = string.find(err, "^.+:(%d+):") line = tonumber(line) if type(value) == "function" then setfenv(value, fenvs.add(value, key, fenvp)) local valueBackup = value value = function(...) table.insert(callStack, { name = key, origin = line, line = indexing.at, from = indexing.from }) local returnVs = {valueBackup(...)} table.remove(callStack, #callStack) return unpack(returnVs) end end if recordedLocals[line][key] or fenvp.locals[key] then fenvp.locals[key] = value else fenvp.parent.setUpvalue(key, value) end end, __index = function(tbl, key) local _, err = pcall(error, "", 3) local _, _, line = string.find(err, "^.+:(%d+):") indexing.at = tonumber(line) indexing.from = fenvp.name return fenvp.locals[key] or (fenvp.parent and fenvp.parent.getUpvalue(key)) end }) return fenvp.env end func = loadstring(contentCopy, fs.getName(progName)) setfenv(func, fenvs.add(func, fs.getName(progName))) local ok, err = pcall(func, unpack(progArgs)) if not ok then printError(err) for ixCall = #callStack, 1, -1 do local call = callStack[ixCall] printf(" '%s' of %i at %i from '%s'", call.name, call.origin, call.line, call.from) end end --]]