Hacker News new | ask | show | jobs
by unbendable 2447 days ago
We use lua 5.3 on an embedded Platform for scripting and it has been a roller coaster ride. Not a fun one, unfortunately. You want luasocket? The stable one is not compatible with 5.3. Packages are sometimes outdated (for years no updates) and there is no replacement. The lua point releases have breaking changes. The source code itself is a macro hell which is hard to debug. And the code is not very readable. The documentation lacks for some topics of you use the c api. I tried to implement some scheduling for c and lua threads.

Oh and one thing if you must use Windows... you better quit right away. I had to help my co worker Installing it with luarocks and it is a mess. to be fair it was easy on my Ubuntu machine.

The thing is lua on embedded has no rival. but god did it cost me some nerves.

5 comments

> The documentation lacks for some topics of you use the c api.

I'm surprised to hear this. I'm doing a lot of Lua and I always thought the documentation is very succinct and complete. I really curious when issues you ran into.

> The thing is lua on embedded has no rival. but god did it cost me some nerves.

You might take a look at https://bellard.org/quickjs/ and https://duktape.org/. The latter seems oddly familiar if you've worked with the Lua C API.

My info might be a bit out of date, but something that surprised me when I looked into it was how little control I had over the embedded lua interpreter from my outer app. I basically had to hand it control by calling lua_pcall, and either wait for a result or manage a timeout from another thread.

I expected to be able to do things like tell Lua to run for X milliseconds or Y opcodes or whatever, but it didn't seem that anything like this existed.

So perhaps my disappointment with the documentation was really just disappointment that the implementation wasn't set up to do what I wanted in the way I wanted to do it. But in the course of coming to this realization, I also found the documentation frustrating and opaque.

Running for X opcodes isn't too difficult, but isn't immediately straightforward either. Instead of calling a function with lua_pcall, start a coroutine using lua_resume. Then, you can have a callback using lua_sethook and the LUA_MASKCOUNT option, which triggers the callback after a fixed number of opcodes. Inside the hook, you make a call to lua_yield. That forces the coroutine that was running to yield control to the outer app. The outer app can then resume the coroutine if desired.

There was a while that I wanted use this to design a programming-based video game, where simulated agents worked in real time. Most of the programming games I have seen use a fixed timeout, and kill any scripts still running after that time. What I was thinking was to instead have the script constantly running, and any slow scripts continue running on the next frame (e.g. all players get 100 opcodes per frame).

Thats clever. I used the same approach with lua_sethook to run a hook every X opcodes and check if time or memory constraints have been exceeded. But instead of yielding I used longjmp to terminate execution (basically like raising an exception and catching it a few levels up in the stack).

It's not bulletproof though and the script can still block e.g. when calling into native code.

Yeah, native code can block, and in those cases requires additional checks in that exposed native code. That runs into all the usual multithreading headaches, and depends on exactly which functions have been exposed to the lua interpreter.

The advantage to this method over longjmp is that the function call is still valid and can be resumed at any point.

Ah. Yeah applying resource limitations outside of memory usage is always a bit awkward. The only really supported way to my knowledge is to externally set a hook and trigger an error when called: https://github.com/lua/lua/blob/master/lua.c#L40-L59. I think this doesn't always work in tight loops though.

I remember patching Lua over 10 years ago for a programming game. I essentially counted VM instructions and stopped executing when reaching the limit. It survived malicious players on a hacker conference back then ;-)

The probably badly aged code is still available here: https://github.com/dividuum/infon/blob/c25eb749b17df3fd9b7a5...

I worked on a product that used Lua and didn’t particularly like it. I’ve never used it, but I do like the syntax and style of Wren (http://wren.io/)
I'm not sure which embedded platform you're using but give AngelScript [0] a look.

I've been using it to extend some C++ code (bsnes emulator) and it's been fantastic. It's so nice to have a C++-like scripting language with natural script-host bindings that I don't have to think hard about creating or spend a lot of time writing marshalling code or worrying about memory layouts. On top of that nice script-host interface you get an actual usable scripting language without silly things like 1-based array indices.

That said, it does not come with any sort of standard packages like luasocket but there is support for defining your own modules.

Depending on the embedded platform you're using, there may not be support for the native calling convention and you may have to fall back to the generic calling convention, or you could roll your own code to accommodate your platform's calling convention.

[0] https://www.angelcode.com/angelscript/

AngelScript is not a transpiler. It's a runtime scripting language with an interpreter designed for easy script reloading at runtime, and I think there's even a JIT out there for it too. It's a valid competitor to Lua for the same use cases Lua serves. The only difference for integration may be that AngelScript is implemented in C++ and not C. Probably a C wrapper API could be (or has been) developed but that might not solve the problem of lacking a C++ compiler for your embedded platform.
I share some of your pain. We use Lua 5.1 on our embedded platform and at one point did development on 5.2.

There's a lot of (seemingly) undocumented changes, even between two minor versions that make you question the semver (and your sanity) at times.

For example, the patterns operate just different enough between the two minors, that I had to resort to dumping everything into JSON and back since that was the only way that one odd case worked similarly between the two versions (albeit sharing the same JSON library).

In regards to the new version, coworker of mine glanced at the 5.4 changelog and could not make out how things had changed, only the fact that they had been changed.

Lua does not follow semver (it never has, and has been around longer than semver)
Lua point releases, 5.1, 5.2, etc. are major releases. The numbering scheme isn't semver. It is only accidentally that Lua even has an ecosystem. It is meant entirely for embedding within a larger application and not used as a stand alone application server.
> The thing is lua on embedded has no rival

What about something like Nim which compiles to C?

https://nim-lang.org/

I just had a short glimpse, but it seems that this is rather "transpiling" to C, right? We needed something that is being interpreted to allow easy updates of scripts.

Comment on all subjects (I will just link it to other commenters): A lot of commenters posted some projects with JS interpreter. But we had to chose something that is stable and is being maintained. The last thing is always hard to argue about because you never know how long maintainers stay commited to the project. But the LUA interpreter is widely used for a lot of (commercial) projects. So there is high chance that it stays for some time. We tried out some other projects, sometimes looking into mailing lists and github issues where we would find open bugs where the interpreter leaks under some circumstances or other problems that you just want to avoid.

LUA seemed a perfect fit and i think still is, but there are still things that didn't work as planned. I really love the concept of the interpreter with the stack. But, again, scheduling is a very complex topic and that is where i found the lua documentation lacking. Especially scheduling between LUA <-> C. I found some projects that solve this issue but then we stumbled upon other issues: Some projects were for 5.1 and didn't work for us. Others used features we didn't have on embedded (pthreads). So we decided to do a very basic scheduling but had to understand more what happens under the hood. And that is where the source code is not easily readable. Don't get me wrong i am convinced that the devs are great at their work, but holy moly you can't use more readable name for you variables? Or the use of macros, is it really neccessary to use it for every conversion?

Nim is compiling to C. It also supports scripting.
> this is rather "transpiling" to C, right?

Yes, my bad.