Today, I'm writing tests. Originally, I planned to write a bunch of tests in Rust to exercise the request handlers, but that felt like a huge pain in the backside, and would've involved a lot of repetition and boilerplate.
Then, I figured: I'll write the tests in #Roto and #Lua! Test the things from that side. Much less boilerplate, but still a lot of repetition.
Instead, I'll be writing a test suite to verify the decisions of a request handler. I'll run it for both engines, #Roto and #Lua. I will still have to write the scripts twice, but I will only write the verification once.
New software release today!
Just published v2.0 of Daemonparts, a collection of unix daemon boilerplate for #Lua. Next version of Nepenthes needed this out.
I think it's unlikely anything not made by me is using it, but it's 2.0 because there are some severe breaking changes.
Anyway if you're interested in daemonizing Lua scripts it's on LuaRocks, and documented here:
https://zadzmo.org/code/daemonparts
A lua hoje parece a ponta de uma unha cortada.
Oh wow, learned a lot from Robin's workshop, very inspiring. I'm definitely going to dive into Lua scripting to streamline my Ardour workflow.
🖥️ My ultra-budget server powering http://websysctl.alfonsosiciliano.net has been running smoothly for the past 2 months. So far, so good!
📈 #Crawlers hit tens of thousands of sysctl parameter pages daily. That's fine, since robots.txt allows it. But why keep requesting non-existent pages as if the site were built with WordPress 😤 ? Fortunately, the stack (#FreeBSD :freebsd: + #OpenResty 🌐 + #Lapis ✏️ + a custom-built #database 📦 ) stays well within the limited resources of my $5/month cloud server.
The code might soon be #OpenSource stay tuned!
#UNIX #sysctl #WebDev #WebServer #ThePowerToServe #coding #Lua #kernel
The @ardour #Lua scripting workshop at #LAC25 #INSA #Lyon has just started. And I am curious to learn how to extend the functionalities of this great free and #OpenSource #DAW in a programmatically way in #realtime.
As @lualang is #CrossPlatform like #Ardour is, you can draft your script on a Windows machine at work and refine it on your #Linux computer later at home, for example.
Let's see how we can automate tasks in Ardour sessions, which otherwise would require another person operating #plugin parameters, session properties, #routing, etc. interactively.
Robin's workshop "Ardour Lua Scripting" has kicked off!
I've been porting https://github.com/2Retr0/GodotOceanWaves to the Recoil Engine/Beyond All Reason. Need to do a bit of work on optimization, a ton of clean up, and then adding some features.
My fish schooling sim(boids) needed a nicer place to live. So I went to work.
Pretty far from done but there is potential.
Unfortunately, there are gotchas there, too.
(fn decide [request]
Outcome.not_for_us)
This #fennel code compiles to the following #lua:
local function decide(request)
return Outcome.not_for_us
end
return decide
The problem here is that I'm not require
-ing this file. I maybe should. Without require, that return
makes little sense, and the decide
function won't be found in the global scope, so...
Error: error converting Lua nil to function
Which makes perfect sense. Except the error message is bad, and needs to be improved.
Today's adventures begin with trying to make #iocaine play well with #Fennel. There's work to be done on this front...
For starters, I don't think I will be able to support running the Fennel compiler as part of the init process. It seems to require debug
and assert
, which I'm not sure I want to make available to the Lua runtime in iocaine.
debug
requires mlua.unsafe_new()
, which in turn requires an unsafe
block, and I'm not comfortable with that. Not even sure how I can make assert available, mlua
doesn't seem to provide that as part of stdlib.
So, next best thing: compiling #Fennel to #Lua ahead of time!
Why Go Rocks for Building a Lua Interpreter via @abnv https://lobste.rs/s/lot0ao #compilers #go #lua
https://www.zombiezen.com/blog/2025/06/why-go-rocks-for-building-lua-interpreter/
Right. Reading the docs: yes, Store
is short lived, and the linker needs to be instantiated for every request too. Both should be cheap.
Currently waiting for a dummy #wasm implementation to compile, one similar to my first #Lua try: a decide
function that returns false
(or in this case, 0
, as i32
).
Curious how this will perform.
After cleaning up a bit of a mess I made, I ran some benchmarks:
Timer precision: 30 ns
handler fastest │ slowest │ median │ mean │ samples │ iters
├─ request_handler_language_lua 279.7 ns │ 279.4 µs │ 349.7 ns │ 417.1 ns │ 9036446 │ 9036446
╰─ request_handler_language_roto 65.17 ns │ 2.958 µs │ 69.11 ns │ 69.93 ns │ 1061654 │ 67945856
Both benchmarks do essentially the same: return Outcome::Garbage
. While #Lua is certainly fast, and is going to be Just Fine for most loads, #Roto blows it out of the water.
The complexity of the script matters little, because the heavy lifting is being done on the Rust side, and is common between the two.
Great to see my first choice justified. Still, gonna keep Lua around, because it's not slow, and is a more familiar language.
I might put it behind a feature flag, though.
A very nice #Lua convenience: I don't need an init()
function! I can just do the init in the body of the script.
function decide(request)
if _G["foobar"]:contains(request:header("user-agent")) then
return Outcome.not_for_us
end
return Outcome.garbage
end
_G["foobar"] = Patterns("test-agent", "curl")
It is also easier to make this kind of stuff available to Lua, from the Rust side:
#[derive(Clone)]
struct Patterns(AhoCorasick);
impl UserData for Patterns {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("contains", |_, this, needle: String| {
Ok(this.0.is_match(&needle))
})
}
}
pub fn register(runtime: &Lua) -> Result<()> {
let constructor = runtime.create_function(|_, patterns: Variadic<String>| {
let ac = AhoCorasick::builder()
.ascii_case_insensitive(true)
.build(patterns.iter()).unwrap();
Ok(Patterns(ac))
})?;
runtime.globals().set("Patterns", constructor)?;
Ok(())
}
(Yes, there's an unwrap()
there, I will get rid of it.)
It's so much simpler than for Roto, because Roto does not support lists yet, so I had to add a list builder too.
Ok, dispatch is in, some quick benchmarks, using a script that does nothing but return Outcome::NotForUs
:
So #Roto wins over #Lua by a sizeable margin here, even with luau-jit
(which, iirc, is supposed to be the fastest).
However, #Lua is fast enough. I'm willing to trade some performance for user convenience, and Lua is at a spot where the performance drop, while noticable, is within acceptable limits, and is offset by the convenience of the language.
In a number of ways, #Lua is going to be a better fit than #Roto: it's a far better known language, and a whole bunch of things are easier to do in Lua.
Do I regret going with #Roto first? Absolutely not. I like Roto's syntax better, and prefer its minimalism over Lua. From what I remember about my prior benchmarks, Roto is also significantly faster. But I'll do some side-by-side comparisons once the Lua support is in a better place, and once I can actually choose which one to use.
Right now I just made a struct that implements the same functions as MeansOfProduction
, and replaced Roto with Lua. That is obviously not how it will work down the road.
Last night I started to wonder: what if I embedded #Lua in #iocaine?
So today I'm going to do that. A first, exploratory hack is compiling now, so I can benchmark it. It doesn't load any external files yet, it's a hardcoded return false
.