I've seen a lot of threads where people talk about using mini JSON (JavaScript Object Notation) parsers, but there are always caveats about the differences betwen JS and Lua.
I've prototyped a little parser for what I call LTN (Lua Table Notation, pronounced "loot'n"), which effectively gives you the ability to specify a Lua table's contents, in Lua source format, inside a string. For instance, if you had this:
player= { name="lone wanderer", desc="alone.\nwandering.\n", x=123, y=456, attrs= { str=5,per=3,edr=5,chr=3,int=4,agi=3,lck=4, hp=100,mp=20 } } |
You could swap out the outer curlies for Lua's multi-line string braces ("[[ ... ]]") and add a call to my parser, omitting the parens since it's a single string param and Lua lets you do that:
player=ltn [[ name="lone wanderer", desc="alone.\nwandering.\n", x=123, y=456, attrs= { str=5,per=3,edr=5,chr=3,int=4,agi=3,lck=4, hp=100,mp=20 } ]] |
This particular case saves 41 tokens.
Problem is, Lua's a bit squidgy as a language, so parsing its format, even with the assumption that everything is cleanly formatted and with support for comments and "[literal]=value" notation not included, costs about 310 tokens. (It's about 350-390 tokens with those, if you need either/both of them, but I think most people wouldn't.)
Would that be a decent quality-of-life tradeoff for anyone, allowing you just to swap Lua init code for a string representing it like that, but at the cost of 310+ tokens? It feels like a lot of tokens for a table parser, but on the other hand, sometimes you can afford to be a little spendy as long as it saves enough tokens overall AND saves you time reformatting the data.
Should I work on this further to clean it up and make it worthy of release? Or nah?
Technical notes:
One reason why my parser assumes everything is cleanly formatted is because you can test the formatting by swapping ltn[[]] back to {} and running it directly to test. I do have a version with error-checking, but again, it adds a lot of tokens to do that.
Also, I could probably make it a little smaller if I removed the option to specify a simple array table like this:
count_to_five_array=ltn[[1,2,3,4,5]] |
Depends on whether a person needs to initialize arrays or just objects.
I can argue that "my" json parser (without error checking) is 309 tokens :)
What I wouldn't trade for is my binding logic, e.g. my parser automatically binds a string to its function/value equivalent from a global table ("_g").
Your version will have trouble doing that given that there is no global metatable (unless an additional table is given to ltn).
Ex:
_g.id=1 _g.update_player=function(self) self.x+=1 self.y+=0.5 end local player=json_parse('{"x":0,"y":0,"side":"id","update":"update_player"}') -- prints 1 print(player.side) -- calls update_player function player:update() |
Other than that, I usually have between 3000-4000 tokens worth of data packed this way. I definitely cannot make any more PICO-8 games without that :/ (well, beside Asteroids!)
As to wether including that in the PICO-8 "language", it feels like artificially expanding token count limit - mixed feeling here...
I do actually have a look-up table for literal values: {["true"]=true,["false"]=false}
. That could be used in the way you describe. I've actually documented it as such in my code. I just didn't mention it because people who use JSON are usually just looking for pure data, but yes, it's an option.
Also, it's down to 310 tokens now. ;) I haven't really tried too hard to optimize it--I was waiting to see if there's enough interest to be worth the effort.
Also also, if it's a factor, this format is considerably more terse than JSON's. Compare these, where I've abbreviated your function call and eliminated the unnecessary parens to be fair:
local player=jsn'{"x":0,"y":0,"side":"id","update":"update_player"}' vs. local player=ltn'x=0,y=0,side=id,update=update_player' |
Sometimes compressed source size matters too.
By the way, as implied, you too could be benefitting from the Lua trick that a function call that takes either a string literal or a table literal doesn't require parens:
local player=json_parse'{"x":0,"y":0,"side":"id","update":"update_player"}' -- also might be good to name the function so it looks like some kind of type prefix: local player=json'{"x":0,"y":0,"side":"id","update":"update_player"}' |
A lot of people don't know about this odd little feature of Lua. It's handy for passing inline tables too:
function init_player( table_of_starting_values ) ... end init_player { name="felice", occupation="optimizer", ... } |
Sometimes it even makes the code look better.
another aspect is how the solution reacts to minification (again, mandatoey for large games).
With json, it requires a dedicated script to replace json fields by their minified counterpart (yuck and prone to failures):
local player=jsn’{“name”:”joe”}’ print(player.name) — minified (incorrect) local x=jsn’{“name”:”joe”}’ print(x.y) — minified (correct) local x=jsn’{“y”:”joe”}’ print(x.y) |
I reckon that your solution would just require removing of comment markers for the lua parser to correctly minify data and references.
Oh, I hadn't even considered the need to accommodate minification, but I think you're right, in that a LTN-aware minifier could look for "ltn+str" and parse+minify said instances with its existing Lua parser.
Mind you, with JSON parsers available, the same is true of both schemes.
The tricksy part would be knowing which set of minified attributes to use, hmm. Not all object types might end up with the same replacements, unless it was a global conversion of, e.g., every "name" attribute with "n".
Well, maybe I'll try cleaning it up in the near future and posting it to see if anyone actually wants to try it and give feedback. It's mostly done, just leaving me with an urge to make it more straightforward to remove features you don't need (e.g. comment support).
The tricksy part would be knowing which set of minified attributes to use, hmm. Not all object types might end up with the same replacements, unless it was a global conversion of, e.g., every "name" attribute with "n".
True for json, not true for ltn as there is no ambiguity.
I'd be happy to test, given I already have a very similar framework in place.
Hi, have you ever continued work on this? Very interested in your parser!
[Please log in to post a comment]