Lua — If error in function, then do something - lua

I feel like this should be simple, but can't seem to solve it. I have a function in Lua that's designed to validate a confirm code in a survey. Basically, if the ID is valid, then we can grab lots of data from that code, but if it's not a valid code, the script will break because it'll be populating nil values.
So, I basically need an error check — if the function can run properly, then run it. If it can't then I need to ask for a new code.
I've tried using pcall which feels like is exactly for this. I'm working off the Lua documentation:
if pcall(foo) then
-- no errors while running `foo'
...
else
-- `foo' raised an error: take appropriate actions
...
end
On my end, that means I have a function:
function populate()
... doing lots here to unencrypt and parse the ID someone gives and populate variables
end
Then I'm running the follwing:
if pcall(populate) then
print('no errors!') -- Just printing as a test, if there's no error, I'll run the script
else
print('Oh snap theres an error!) -- I'll change this to ask the user for a valid ID and then try again
end
What am I missing? I know it's going to be simple. But the last part of my code always returns the "Oh snap..." no matter what.
Thanks in advance, I have a super complex code running that I was able to build from just reading responses to other questions, but can't seem to get this simple part to work. Entirely possible I'm missing the point of pcall.

What do you expect?
If i am unsure what happen or which return value i should use for another condition i normally test/check in Lua Standalone...
€ lua
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
> pcall(string.gsub,'foo','%l','bar') -- Check for lowercases
true barbarbar 3
> pcall(string.gsub,'foo','%u','bar') -- Check for uppercases
true foo 0
> -- In this case i have to use maybe the third return value to decide what to do?
> -- OK -- Lets go...
> a, b, c = pcall(string.gsub,'foo','%u','bar') -- Check for uppercases
> if a and c == 0 then print('Only Kiddies here!') return false end
Only Kiddies here!
false
>

Related

Lua io.popen freezing after a while of reading a log file

I'm attempting to constantly read and parse a log file (Minecraft log file) by using io.popen in tandem with Ubuntu's tail command so that I can send some messages upon certain events.
Now, I have mostly everything working here, except one small issue. After a while of reading, the entire program just freezes.
Here is the relevant code:
-- Open the tail command, return a file handle for it.
local pop = io.popen(config.listen_command)
-- Simply read a single line, I've pulled this into its own
-- function so that if this ever needs changing I can do so
-- easily.
local function get_line()
logger:log(4, "READ LINE")
return pop:read("*l")
end
-- For each line in the log file, check if it matches any
-- of a list of patterns, return the matches and the
-- pattern information if so.
local function match_line()
local line = get_line()
logger:log(4, "Line: %s", line)
-- This all works, and I've tested that it's not freezing
-- here. I've just included it for completion of the call
-- -stack.
for event_type, data in pairs(config.message_patterns) do
for event_name, pattern in pairs(data) do
local matches = {line:match(pattern)}
if matches[1] then
return event_type, event_name, matches
end
end
end
end
-- The main loop, simply read a line and send a message
-- if there was a match.
logger:log(4, "Main loop begin.")
while true do
local event_type, event_name, matches = match_line()
-- ...
-- The rest of the code here is not relevant.
config.listen_command = "tail -F --lines=1 latest.log"
The issue is in the get_line function. After a while of reading the log file, it completely freezes on the pop:read("*l"). It prints the READ LINE message, but never prints the Line: <whatever data here> line.
This is a really strange issue that I've been getting really confused about. I've tried swapping to different distributions of Lua (Luvit, LuaJIT, Lua) and a very large amount of debugging, changing small things, rerunning, ... But I cannot think of anything that'd be causing this.
Perhaps there's something small I've missed.
So my question here is this: Why is pop:read("*l") freezing, even though more data is being outputted to the logfile? Is there a way to fix this? Perhaps to detect if the next read will freeze indefinitely, so I can try closing the popen'ed file and re-open it (or to preferably stop it happening altogether?)

Sourcing nvim.init does not source required Lua files

I have neovim 0.7.0 running and my .vimrc is at ~/.config/nvim/init.vim
I also have the following file: ~/.config/nvim/lua/statusline.lua with one line of code:
print('message from statusline.lua')
Inside init.vim I have:
echo 'from init.vim'
lua require('statusline')
When I start nvim I get both messages printed out ('from init.vim' and 'message from statusline.lua') which is what I would expect.
When I run :source $MYVIMRC I only see 'from init.vim'. I would expect the other message ('message from statusline.lua') to appear as well.
I assume this means any changes I make in statusline.lua will not take effect when I run :source $MYVIMRC. How should I source my init.vim file plus any files it requires without closing and restarting neovim?
Either invalidate cache entry by appending return false to the end of a module.
Or don't use require at all, as you need neither cache nor path search anyway. E.g.
for k, v in ipairs(vim.fn.glob("~/.config/nvim/init.d/*.lua", false, true)) do
dofile(v)
end
P.S. Lua is not a "config tool". It is a full-featured programming language. If you don't like wasting your time by learning it properly (i.e. reading books and tutorials) you're highly suggested to use VimScript instead. It has some "dark corners" of its own but it is much better suited for writing config.
It depends on the design of your statusline.lua and knowledge about Lua' module loader system.
It looks, because i have to riddle about statusline.lua, that it nothing returns.
Because the return is going into package.loaded and same require in same session looks first there for statusline
So give following a try...
-- statusline.lua
print('message from statusline.lua')
return 'message from package.loaded.statusline'
I have tested above with...
$ lua -i
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
> require('statusline')
message from statusline.lua
message from package.loaded.statusline ./lua/statusline.lua
> require('statusline')
message from package.loaded.statusline
> require('statusline')
message from package.loaded.statusline
EDIT
Another design for doing something usefull...
-- ~.config/nvim/init.vim
lua print("init.vim")
lua dump = require("dump")
And...
-- ~/.config/nvim/lua/dump.lua
local dump = function(tab)
for key, value in pairs(tab) do
print(key, '=>', value)
end
end
return dump
Than you have a table viewer and you can see where the functions and tables come from with...
:lua dump(_G) -- The global environment table
:lua dump(vim) -- The nvim stuff (aka Module)
:lua dump(vim.api) -- The nvim API functions (aka Library)
:lua dump(jit) -- The Just In Time Compiler ;-)
:lua dump([Table Name]) -- Any table that looks interesting
:lua dump(package.loaded) -- The required or requireable stuff
Above function can be executed without defining dump first with: :lua require('dump')(_G)
So: First require loads dump.lua into package.loaded.dump and returning it and every further require returning: package.loaded.dump
If you have an sharp eye than take a look on _G.dump thats only a reference (pointer/link) to package.loaded.dump.
EDIT2
Preparing dump.lua for using it with vim.api.nvim_input()
-- ~/.config/nvim/lua/dump.lua
local dump = function(tab)
local tmp = ''
for key, value in pairs(tab) do
tmp = tmp .. ('%s %s %s\n'):format(key, '=>', value)
end
return tmp
end
return dump
Now the dump function returning a string and the output can be loaded into nvim with: :lua vim.api.nvim_input('i') vim.api.nvim_input(dump(vim.api))
Since many nvim API functions returning a table the dump function becomes handy with...

How to add a page break before header automatically in pandoc

I need to convert a markdown document into a Word document.
I need it so that before each header there is a page break.
For example, here, a page break would be inserted before # Heading 1 and before # Heading 2. I wouldn't mind if it skipped the very first one if there is no content before it in the document.
# Heading 1
Hello
# Heading 2
World
I have found this Lua filter, but, as far as I understand, it's just for Latex and it's not automatic.
I have tried to write a Lua script myself, but failed, because I do not understand well how pandoc works.
For example, I have tried returning a Para with the contents of the previous one + a page break from the Header function like this:
function Header(el)
if el.level == 1 then
local t = { pandoc.RawBlock('openxml', '<w:p><w:r><w:br w:type="page"/></w:r></w:p>'), el }
return pandoc.Para(t)
end
return el
end
However, it just ends up wiping all of the headers.
I have tried a few other alterations of the same idea, neither of which worked.
The trouble in Lua is mainly the fact that there is no static analysis and no Intellisense. I may be doing something completely inadequate in this code without any compiler errors and without even realising it.
I believe the following should fix that:
local pagebreak = '<w:p><w:r><w:br w:type="page"/></w:r></w:p>'
function Header(el)
if el.level == 1 then
-- return both the page break and the header as a list
return { pandoc.RawBlock('openxml', pagebreak), el }
end
-- No `return el` needed, as not returning any value is the same as
-- returning the original value.
end
The error reporting of pandoc during unmarshalling, i.e., while retrieving objects from Lua, is not great, to say the least. Ideally, pandoc should report the type error that comes from wrapping a list of "blocks" in a Para, as that constructor should only accept "inlines".
In case you are curious: I'm working on fixing this by switching to a different marshalling strategy, see this PR. It's taking a while, but we'll get there.

Sandboxing Lua functions

Before I start let me clarify that I've looked through the existing questions regarding sandboxing, but none of the solutions I found really seemed to work for me.
What I am trying to implement is not necessarily a completely safe sandbox to run unsafe code, but provide an environment that emulates the functions of a game (Star Wars Empire at War to be exact) I'm modding that can be extended using Lua scripts. The game provides a bunch of global functions that I want to implement in this environment to be able to test my game extensions without launching the game itself, since its debugging capabilities are quite poor. The game also has some quirks. The relevant issue in this case is that using upvalues will produce corrupt save games, so this is why our Lua modules have to be globals. Since my game scripts are not contained in a single file and can require other files, call and declare their own functions using setfenv() to set an environment does not really work for me, since that applies only to the function you specify and not to any other functions it might call.
Now for my code itself. So far I've tried to implement a simple sandbox by simply deep cloning _G and restoring it to its former state afterwards (this is still WIP, I realize that you can still do table and environment modifications or potentially break out of the sandbox that aren't covered by this approach; as I said, this is not intended to run unsafe code).
This is in sandbox.lua:
local function run(func)
-- tested deep_clone with luassert's assert.are_same(), so this should be fine
local g_clone = deep_clone(_G)
-- coroutine is needed because one of the game functions needs
-- to be able to interrupt the script
coroutine.wrap(function()
func()
end)()
for k, v in pairs(_G) do
if not g_clone[k] then
_G[k] = nil
else
_G[k] = g_clone[k]
end
end
end
And this is my test script:
sandbox.run(function()
require "src/declare_global" -- this declares A_GLOBAL = "TEST"
print(A_GLOBAL) -- prints "TEST", everything is fine
end)
print(A_GLOBAL) -- prints nil, as intended
-- as I've restored _G to its former state I should be able to require
-- the script again!
require "src/declare_global"
print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- prints nil
print(A_GLOBAL) -- prints nil
As you can see my problem is that requiring the same file that simply declares a global after I did it in my sandbox doesn't work anymore. So there's probably an issue with restoring the state of _G, but I don't have any clue what it could be. The require call itself seems to work, though. If the script I'm requiring returns a function, I can still store it in a variable and execute it.
I'd really appreciate any suggestions on how to fix this.
I found a solution to my problem. However, I feel this is an inefficient way to achieve what I want, so if somebody has a better suggestion I would love to hear it.
#EgorSkriptunoff's comment on my original question regarding the "shallow" the restoration of _G pointed me in the right direction, so I ended up writing a "deep restore" function, which seems to work fine with what I have tested so far.
local known_tables = {}
local function deep_restore(tab, backup)
known_tables[tab] = true
for k, v in pairs(tab) do
if backup[k] == nil then
tab[k] = nil
elseif type(v) == "table" and not known_tables[v] then
deep_restore(v, backup[k])
elseif not type(v) == "table" then
tab[k] = backup[k]
end
end
end
local function run(func)
local g_clone = deep_clone(_G)
coroutine.wrap(function()
func()
end)()
deep_restore(_G, g_clone)
known_tables = {}
end
Results:
sandbox.run(function()
require "src/declare_global"
print("Printing inside sandbox: "..A_GLOBAL) -- prints TEST
end)
print("This should be nil: "..tostring(A_GLOBAL)) -- nil
print("package.loaded should be nil: "..tostring(package.loaded["src/declare_global"])) -- nil
print("\nRequiring again...")
require "src/declare_global"
print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- true
print(A_GLOBAL) -- TEST

Run script instructions from external source in Lua

Ok, I'm wanting to know if there's a way of running scripts from an external source with Lua. Be it another file in .txt format or from pastebin, what have you, and run the code and wait for said code to finish, and then continue on with the rest of the function. I'm not quite sure at all about how it'd work, but this is basically the idea I'm going by and isn't actual code.
function runStuff()
print("checking for stuff")
run.script(derp.txt)
wait for script
if run.script == finished
continue program
elseif nil
print("looks like this script sucks.")
end
end
runStuff()
And for example what "derp.txt" contains is:
function lookFile()
if herp.txt exists then
rename herp.txt to herp.lua
else
nil
end
end
lookFile()
I'm still new to Lua so I'm coding like a moron here, but you get my picture hopefully. I'm working on a project that'll act like an installer package for repositories based from pastebin or anywhere else that'll supply raw format outputs of lua scripts and I'm going to use that idea for it to call on scripts to run externally. So when I supply a "First Time Run" version of the program, it'll call out to a lua script that'll call to another lua script and install that script, then close.
This is for minecraft, mind you. ComputerCraft made me take interest in Lua, but anyway, hopefully you got the gist of what I'm trying to figure out. Hopefully that's doable and if not, I'll just have to figure something else out.
To load and execute a Lua code fragment you can use something like this:
local http = require("socket.http")
local response, err = http.request("url to some Lua code")
if not response then error(err) end
local f, err = (loadstring or load)(response)
if not f then error(err) end
print("done with "..response)
-- make sure you read on (in)security implications of running remote code
-- f() -- call the function based on the remote code
You probably need to use sandboxing.

Resources