Prettier code formatter for monkey-c

[EDIT]

The plugin seems to be working well (ok, there's room for debate, but it does for monkeyc what prettier does for javascript).

 - if you're already using npm, you can 'npm install --save-dev @markw65/prettier-plugin-monkeyc', and install the VSCode Prettier plugin.

 - if you just want to use prettier for monkeyc code, and only want to use it inside of vscode, and dont want to mess around with npm, you can just install https://marketplace.visualstudio.com/items?itemName=markw65.prettier-extension-monkeyc and it should just work.

[/EDIT] 

So far there doesn't seem to be a code formatter for monkey-c. I've been writing quite a lot of monkey-c code recently, and I've really missed the auto-formatting provided by prettier for javascript (and many other languages).

A few days ago I decided to look into how to extend prettier. javascript isn't too different from monkey-c (or at least, monkey-c maps fairly cleanly onto a subset of javascript/typescript), so it seemed like it should be doable. I found a parser generator which already had a javascript grammar which produced an ast in the format prettier expects. In fact, the ast was fully compatible with prettier's estree printer. So I started from there, and modified it to parse monkey-c instead. Then I was able to delegate most of the printing to the existing estree printer in prettier. So after a couple of days of fiddling around I have something that prettifies all of my code in an acceptable way, and the code still works...

I've attempted to test it more thoroughly by running it on all of the Garmin sdk samples. It took a while to get everything to run through without errors (I guess there are a lot of features I don't use in my own code), and then a bit longer to get it to produce compilable code(!). But finally everything compiles, and if I compile in release mode, the prettified binaries are identical to those produced by the original code - so I didn't change the meaning of the code.

I created a GitHub repo to show what it does to the Garmin samples. There's a branch, original, pointing to the code as it was in the sdk. Then I prettified everything (branch pretty). You can browse the code, or just look at the actual changes.

I need to do a bit more cleanup and testing on my code, but I hope to publish the it on GitHub and npm as a Prettier extension within a few days - then it will be usable in VSCode via the Prettier extension. Meanwhile, feedback on the changes it made to the Garmin sample code would be appreciated.

Top Replies

All Replies

  • It's definitely doable, but as I said, would require a whole new analysis pass.

    One other issue I just remembered, the xml library I'm using doesn't record source positions, so although such a pass could rewrite the resources to omit unused strings (with sufficient work), producing diagnostics/syntax highlighting would require finding a better xml library. I already hit this issue trying to report warnings about the manifest file (eg referencing non-existent devices). Again, not a huge issue (I'm sure there *is* a better library somewhere), but would require rewriting all my existing xml code...

  • Thanks for you work. Is there some ways to buy you a beer or coffee

  • How does this compare to the optimization in the compiler 2 betas or in the newer 4.2.0 announced at the GDVC?  There was a whole breakout that talked about the optimization that's now built in.

  • How does this compare to the optimization in the compiler 2 betas or in the newer 4.2.0 announced at the GDVC?

    I posted code / data size results against 4.1.4-compiler2-beta2 a while ago. There's a lot of data to digest, but the high level summary is that my optimizer with -O0 is almost always better than just using the Garmin compiler at -O2, and my optimizer with -O2 is usually better still (although typically by a smaller margin).

    My tests with the 4.2.0 beta suggest that it's a little better than the first betas, but not by much. I will probably regenerate that table at some point, but it's not fully automated, so takes quite a lot of work.

  • Thanks for you work. Is there some ways to buy you a beer or coffee

    It's a fun project. And thanks for the kind words - its nice to know people are finding it useful.

  • And if I remember well then those numbers are statistics of many "regular" apps, with code made without using any of the specific features of the plugin, so if someone uses them then it can be even better.

  • those numbers are statistics of many "regular" apps, with code made without using any of the specific features of the plugin

    That's true. They're all just random projects I found, and none have been modified to take advantage of my optimizer. So as you say, in a project where you took advantage of its features - such as inlining - you could probably do better (and indeed, thats what I've found with my own project).

  • I compared my app's size with 4.1.5 vs 4.1.6 compiler and with and without the prettier monkey c, and I found (at least in my app) that the optimizer produces worse code with 4.1.6 than with 4.1.5

    4.1.5 native:  global:964,code:12761,data:4671,entrypoint:4007 mem usage:27.2/peak:28.5
    4.1.6 native:  global:964,code:12695,data:4803,entrypoint:3911 mem usage:27.2/peak:28.5
    4.1.5 prettier: global:849,code:11093,data:4049,entrypoint:3984 mem usage:24.8/peak:26.0
    4.1.6 prettier: global:849,code:11078,data:4253,entrypoint:3888 mem usage:24.9/peak:26.1

    as you can see the mem usage and peak mem usage did not change between 4.1.5 and 4.1.6 native but increased by 0.1k with prettier (code+data+entrypoint increased by 97 bytes)

  • So to be clear, 4.1.6 with my optimizer is better than either 4.1.5 or 4.1.6 *without* my optimizer. Its just that 4.1.6 + optimizer is worse than 4.1.5 plus optimizer?

    Also, by the looks of things, 4.1.6 *without* my optimizer seems to have 132 extra data bytes vs 4.1.5. But *with* the optimizer, its an extra 204 data bytes.

    So a few things to note.

    • 4.1.6 is the first official release of compiler2. ie it has its own optimizer. I'm guessing you're not invoking it (add -O2 to the compiler options).
    • if you specify -O0, it reverts to using compiler1 - so you should get (pretty much) exactly the same results as 4.1.5
    • if you're trying to compare sizes, you should use a release build (maybe you did - but just saying). Debug builds seem to have embedded data that depends on path lengths - and file paths tend to be longer in the bin/optimized/group-xxx/... folders.
  • Also, with 4.1.6, it looks like release builds default to -O2, while debug builds default to -O1