A Weird Imagination

JSON to Lua

The problem#

The Factorio documentation includes a machine-readable version of the prototype API documentation. I wanted to be able to access that information from within a mod, which required somehow giving Lua access to a JSON object.

The solution#

Install my branch of json2lua and run it on your JSON file:

$ npm install git+https://github.com/dperelman/json2lua.git\#feature/string-escaping
$ npx json2lua "FILE.json" "FILE.lua"

Note the output is a Lua table literal, so you'll have to add code to actually assign it to a variable to be able to use it.

The details#

JSON interpreter in Lua#

As Factorio uses JSON for things like blueprint strings, it includes a built-in function game.json_to_table(json) for parsing JSON to a Lua table. Which isn't actually useful to us because the game object is part of the "Runtime Stage" (which "takes place alongside normal gameplay") and the prototype API is most useful during the Prototype Stage.

I could have copied in that or some other Lua-implemented JSON parser, but I still would have had to figure out how to get the mod access to the JSON file. If I were going to wrap it into a Lua string, I figured I might as well figure out how just convert it into a Lua table.

Converting JSON to Lua#

Since interpreting the JSON inside the mod wasn't looking straightforward, I decided to take a different approach of converting it to Lua before the mod starts. Unsurprisingly, I wasn't the first person to want to do this and I found multiple JSON-to-Lua converters including the npm package json2lua. Surprisingly, none of the ones I tried actually handled string escaping properly; they just assumed wrapping the literal string value with "s would be sufficient, which fails on any string that contains " or any special characters like new lines.

So I made my own branch1 where I modified json2lua to handle escaping strings correctly. Luckily, Lua's string escaping is well-designed to make this easy since if you use [[...]] style string literals then any characters are allowed between the brackets and are interpreted literally. If the string does contain ]], then you can use [=[...]=] instead, up to as many =s as you want. So the code to escape a string str is simple:

var i = 0;
var end = ']]';
do {
    end = ']' + '='.repeat(i) + ']';
} while (str.includes(end));
return '[' + '='.repeat(i) + '[' + str + end;

Getting the machine-readable prototype API into the mod#

Now that I had figured out how to convert prototype-api.json into a Lua table, I made a script to automate the process. In my mod, convert-to-lua.sh downloads prototype-api.json from the Factorio website (unless it's already been downloaded), downloads my version of json2lua, runs it, and outputs a file prototype-api.lua which assigns that Lua table to a global, ReflectionLibraryMod.prototype_api. That way I don't have to include the prototype information in the source repository of my mod and it's easy to update whenever Factorio updates it.


  1. I did submit a pull request, but the project has has no activity for 5 years, so I'm not expecting it to get accepted. 

Comments

Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.

There are no comments yet.