TRANSMUTRIX

2024 April 07

MoonScript LÖVE Template |> LuLPeg Issues & Fix

So it turns out that LuLPeg's differences to regular LPeg are enough to break MoonScript in some common use cases, but not others. For example, extending a class from another module is broken, but extending a class in the same module works.

This is what I get for not running MoonScript's test suite in LÖVE initially!

Weird Errors

The errors I saw at first were bizarre: An "unable to parse" error for the first line of an imported module, regardless of the line's contents. These errors only appeared when calling require on a MoonScript module containing a class that extended another class in a different module.

First, I ran moonc on the project and ran that in LÖVE, and all was well, confirming that the issue was not my MoonScript code itself.

Then, I manually compiled the offending file inside LÖVE (using the splatted MoonScript and LuLPeg), and used VS Code to diff the versions:

-- try manually loading this and check the generated code...
readFile = (path) ->
  file = assert io.open(path, "rb")
  content = file\read '*all'
  file\close!
  content

writeFile = (path, contents) ->
  file = assert io.open(path, "w")
  file\write contents
  file\close!

do
  parse = require 'moonscript.parse'
  compile = require 'moonscript.compile'

  moon_code = readFile './LiveTest.moon'
  print "----- Moon Code ------"
  print moon_code

  print "----- Lua Code ------"
  tree, err = parse.string moon_code
  unless tree then error "Parse error: " .. err
  lua_code, err, pos = compile.tree tree
  unless lua_code then error compile.format_error err, pos, moon_code

  print lua_code
  writeFile "LiveTest_2.lua", lua_code

  love.event.quit!
  return
    

Hm, that doesn't look right!

Digging Around

At this point, I still wasn't sure what was responsible for the bad code generation. After all: My local Lua installation is Lua 5.4, I installed MoonScript with Luarocks, I had to manually update MoonScript's dependencies for Lua 5.4, and LÖVE is using LuaJIT, based on Lua 5.1, not vanilla Lua. There are a lot of moving parts! :)

So, first, I went to get the LPeg source code, but the site was down at the time, so I instead decided to try running my LÖVE project with the version of MoonScript installed on my system, in case it was an older version than what I grabbed from GitHub. This made no difference to the generated code.

I then tried to install Lua 5.1 through homebrew, so that I could then install Luarocks against that, then install LPeg with Luarocks, then get the build artifact for LPeg, specifically built against a Lua 5.1 install, to test with LÖVE.

The homebrew formula for Lua 5.1 was disabled. I tried to manually edit it, but it wasn't on my local disk, so I couldn't. I then went to check LPeg's webpage again, and this time, eventually, it loaded, and I was able to get the source code.

Do you feel productive yet? I sure do. :)

LuaJIT & LPeg From Source

Once I had the LPeg source, I needed a Lua to build it against, so I grabbed LuaJIT since that's what LÖVE is using on dev platforms. Building each required reading and minorly editing their makefiles, which is fine, whatever.

I finally had an lpeg.so built for Apple Silicon that I could dump in my project directory and load in LÖVE instead of LuLPeg, so I re-ran my code gen experiment, and lo and behold, the output was correct.

So the issue was confirmed to be LuLPeg's divergence from LPeg. Crud.

This is Frustrating

My goal with this project was to make MoonScript more accessible to people who are already interested in LÖVE but maybe lack the technical "dealing with bullshit" skills necessary to successfully install Luarocks and MoonScript on say, Windows 10.

My hope was to give people a ZIP archive they could unpack on any machine where LÖVE would run, and just write MoonScript for it. MoonScript, and Lua in general, are a wee bit of a "Linux Club" where users without a certain level of skill and knowledge aren't able to try things out. I want Lua to be more mainstream for beginners. I want MoonScript to be more mainstram, too.

I don't dislike small, insular communities; in fact I prefer them! But I do dislike when communities are small or insular entirely because of unnecessary tech friction.

Solutions

The real solution would be to fork LuLPeg and make it into a 1:1 replacement for LPeg, where every unit test passes. Alternatively, one could maybe patch MoonScript to work with whatever LuLPeg currently does, but this would be less ideal from a maintenance standpoint.

At the moment, I don't have the time to do either of those things.

Temporary Solution

For now, I have attempted to keep the "turnkey" spirit of the project template by providing native LPeg for Mac (ARM and x64) and Windows (x64), and still falling back to LuLPeg on other platforms but printing a warning.

I did it this way to try to cover the most common set of machines people will be using the template on. This way, in the most common cases, it's still a setup that should work out of the box.

I wasn't sure what range of architectures I would need to include for Linux users, for example, since we all love putting Linux on every machine ever to be manufactured. Linux users are also more easily positioned to install LPeg on the cli and just grab the artifact afterwards.

Adding platforms|architectures to the template is a matter of building LuaJIT and then LPeg for that platform and adding it into the require path hoodoo in main.lua, and I welcome any additions folks would like to contribute.

Download & Links

You can get the project template here:

>>> Project Template LPeg Source LuaJIT Source <<<