Big update to prettier-extension-monkeyc

I've posted about prettier-extension-monkeyc before, but I've added a bunch of new features that developers will probably like (well, I've been missing them, so maybe you have too).

The new features it implements for VSCode include:

  • Goto Definition. Point at a symbol, Ctrl/Cmd click, and it will take you to the definition. Or F12
  • Goto References. Right click on a symbol and select "Goto References". It will show you all the references. Or Shift-F12
  • Peek Definition/Peek References. Same as above, but in a popup window so you don't lose your place in the original document.
  • Rename Symbol. Right click on a local, function, class or module name, and select "Rename Symbol". It will rename all the references. It doesn't yet work for class members/methods.
  • Goto Symbol. Type Ctrl/Cmd-Shift-O and pick a symbol from the drop down (which has a hierarchical view of all symbols in the current file). This also appears as an outline across the top of the file.
  • Open Symbol By Name. Type Ctrl/Cmd-T, then start typing letters from a symbol name. A drop down will be populated with all matching symbols from anywhere in your project.

Older features include a prettier based formatter for monkeyc, and a monkeyc optimizer that will build/run/export an optimized version of your project.

[edit: My last couple of replies seem to have just disappeared, and the whole conversation seems to be in a jumbled order, so tldr: there's a new test-release at https://github.com/markw65/prettier-extension-monkeyc/releases/tag/v2.0.9 which seems to work for me on linux. I'll do more verification tomorrow, and push a proper update to the vscode store once I'm sure everything is working]

  • I don't think that the constant folding can explain this

    Then maybe it really is the signature check, and the "out of memory" error is just a bogus error message. What device are you trying to run on? Perhaps newer devices do care about the signature?

    actually I don't remember if I did a build or just copied the already existing prg to the device

    So for me, that seems to work. But the docs explicitly say that you have to do a "device" build, rather than a simulator build if you're going to run on a device. With the optimizer, you would have to use a task in tasks.json, and include 

    "simulatorBuild": false
    in the task definition.
    So maybe that's the issue?
  • No, this is an old fenix6. And yes, that may be the issue. I'll recheck it. So "Export Optimized Project" isn't ok? Do you have an example tasks.json? I've never seen one, I only have launch.json

    Also I've found a new bug:

    (:release, :inline)
    function log(msg as String) as Void {}
    (:debug, :inline)
    function log(msg as String) as Void {
        if (LOG) {
            logRelease(msg);
        }
    }
    
    function logRelease(msg as String?) as Void {
        var time = timeFormat(Time.now());
        System.println(time + " " + msg);
    }
    
    (:no_inline)
    function timeFormat(moment as Moment) as String {
        var time = Time.Gregorian.info(moment as Moment, Time.FORMAT_SHORT);
        var ms = System.getTimer() % 1000;
        return "" + time.hour.format("%02d") + ':' + time.min.format("%02d") + ':' + time.sec.format("%02d") + "." + ms.format("%03d");
    }
    
    class Foo {
        hidden function foo(bar as String) as Void {
            log("foo: " + bar);
        }
    }

    ERROR: fenix6: Foo.mc:391,4: Undefined symbol ':msg' detected.

    the optimized-debug code:

      hidden function foo(bar as String) as Void {
        logRelease(msg);
      }
    

  • So "Export Optimized Project" isn't ok?

    "Export Optimized Project" produces a .iq file, so yes, that should be fine. Its both building for specific devices, and the signatures get updated.

    "Build Optimized Project" or "Build and Run Optimized Project" build for the simulator (so in theory the .prg files may not work on a device), and they don't fix the signature after the build.

    Example task:

    {
      "type": "omonkeyc",
      "device": "fr255",
      "simulatorBuild": false,
      "releaseBuild": true,
      "label": "FR255 Device Build",
    },

    I'll take a look at the bug...

  • ERROR: fenix6: Foo.mc:391,4: Undefined symbol ':msg' detected.

    I don't get this with your example. To make your example work, I added various imports at the top, and a "const LOG=true". But then "hidden function foo" is removed entirely. If I add eg an initialize function that calls foo, or use (:keep) to prevent foo from being removed, I get:

      hidden function foo(bar as String) as Void {
        logRelease("foo: " + bar);
      }
    

    as expected.

    Can you make a standalone example that builds, and has the issue?

  • import Toybox.Lang;
    import Toybox.System;
    import Toybox.Time;
    
    (:debug) const LOG = true;
    (:release) const LOG = false;
    
    (:release, :inline)
    function log(msg as String) as Void {}
    (:debug, :inline)
    function log(msg as String) as Void {
        if (LOG) {
            logRelease(msg);
        }
    }
    
    function logRelease(msg as String?) as Void {
        var time = timeFormat(Time.now());
        System.println(time + " " + msg);
    }
    
    (:no_inline)
    function timeFormat(moment as Moment) as String {
        var time = Time.Gregorian.info(moment as Moment, Time.FORMAT_SHORT);
        var ms = System.getTimer() % 1000;
        return "" + time.hour.format("%02d") + ':' + time.min.format("%02d") + ':' + time.sec.format("%02d") + "." + ms.format("%03d");
    }
    
    class Foo {
        function initialize() {
            foo("FOO");
        }
    
        hidden function foo(word as String) as Void {
            log("foo: " + word);
            var msg = "bingo!";
        }
    }

    OK, now it's pretty clear what causes it. The next line :) 

  • Wow. It actually has nothing to do with inlining. If you write

    function bar(bar as String) as Void {
      {
        var msg = bar;
        log(msg);
      }
      var msg = "foo";
    }

    Then at the end of the block that declares the second "msg" variable, the optimizer notices that msg wasn't used, and removes all assignments to it from within the block, including inner blocks. I remember thinking when I wrote this that I didn't need to check for that case because Garmin's compiler doesn't allow multiple variables with the same name in the scope at any given time... but of course, in this case there is only ever one live variable named "msg", so the compiler allows it.

    The connection with inlining is that when the compiler inlines "log", it creates an inner block, with an inner "msg" variable, just like the above example.

  • The post build optimizer doesn't bother to fix the signature

    I misspoke. It always fixes it, whether you're exporting, or just running locally.

  • New version v2.0.79:

    • Fix a bug in cleanupUnusedVars that could cause the wrong variable to be removed
    • Allow variables mixed with literals in jungle files
    • Allow Char as operands to relational operators
    • Include Null in dictionary return type
    if I missed any of your recent reports, would you mind posting issues at https://github.com/markw65/monkeyc-optimizer/issues. I'm less likely to lose track of them there.
  • added all the missed ones (even ones that you probably haven't missed, but kept for later) and added a new one about concatenating or adding all types of objects to string.

  • I did now "Monkey C: Build for device" and even then I go this "Signature check failed on file" error in CIQ_LOG.BAK